]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
mdhelp: allow to override (extend) property docs - example - DVR config
authorJaroslav Kysela <perex@perex.cz>
Wed, 6 Apr 2016 19:18:53 +0000 (21:18 +0200)
committerJaroslav Kysela <perex@perex.cz>
Wed, 6 Apr 2016 19:19:38 +0000 (21:19 +0200)
Makefile
docs/property/postprocessor.md [new file with mode: 0644]
src/dvr/dvr_config.c
src/idnode.h
src/prop.c
src/prop.h
src/webui/doc_md.c
src/webui/static/app/dvr.js

index 209699c49730d3b97ac25d9c6e414ae07730c061..cd45cb9a7a2f0fafd6319790db159ff053f0dc2c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -547,9 +547,11 @@ SRCS-yes  += src/docs.c
 I18N-C    += src/docs_inc.c
 I18N-DOCS  = $(wildcard docs/markdown/*.md)
 I18N-DOCS += $(wildcard docs/class/*.md)
+I18N-DOCS += $(wildcard docs/property/*.md)
 I18N-DOCS += $(wildcard docs/wizard/*.md)
 MD-ROOT    = $(patsubst docs/markdown/%.md,%,$(wildcard docs/markdown/*.md))
 MD-CLASS   = $(patsubst docs/class/%.md,%,$(wildcard docs/class/*.md))
+MD-PROP    = $(patsubst docs/property/%.md,%,$(wildcard docs/property/*.md))
 MD-WIZARD  = $(patsubst docs/wizard/%.md,%,$(wildcard docs/wizard/*.md))
 
 #
@@ -681,6 +683,11 @@ $(BUILDDIR)/docs-timestamp: $(I18N-DOCS) support/doc/md_to_c.py
           $(MD-TO-C) --in="docs/class/$${i}.md" \
                      --name="tvh_doc_$${i}_class" >> src/docs_inc.c || exit 1; \
         done
+       @for i in $(MD-PROP); do \
+          echo "Markdown: docs/property/$${i}.md"; \
+          $(MD-TO-C) --in="docs/property/$${i}.md" \
+                     --name="tvh_doc_$${i}_property" >> src/docs_inc.c || exit 1; \
+        done
        @for i in $(MD-WIZARD); do \
           echo "Markdown: docs/wizard/$${i}.md"; \
           $(MD-TO-C) --in="docs/wizard/$${i}.md" \
diff --git a/docs/property/postprocessor.md b/docs/property/postprocessor.md
new file mode 100644 (file)
index 0000000..12f65a5
--- /dev/null
@@ -0,0 +1,32 @@
+: Command to run after finishing a recording. The command will be run in
+  background and is executed even if a recording is aborted or an error
+  occurred. Use the %e error formatting string to check for errors, the
+  error string is “OK” if recording finished successfully.
+
+ Supported format strings:
+
+
+Format | Description                               | Example value
+:-----:| ----------------------------------------- | -------------
+`%f`   | Full path to recording                    |  /home/user/Videos/News.mkv
+`%b`   | Basename of recording                     |  News.mkv
+`%c`   | Channel name                              |  BBC world
+`%O`   | Owner of this recording                   |  user
+`%C`   | Who created this recording                |  user
+`%t`   | Program title                             |  News
+`%s`   | Program subtitle                          |  Afternoon
+`%p`   | Program episode                           |  S02.E07
+`%d`   | Program description                       |  News and stories…
+`%e`   | Error message                             |  Aborted by user
+`%S`   | Start time stamp of recording, UNIX epoch |  1224421200
+`%E`   | Stop time stamp of recording, UNIX epoch  |  1224426600
+`%r`   | Number of errors during recording         |  0
+`%R`   | Number of data errors during recording    |  6
+
+*Example usage*
+
+To use special characters (e.g. spaces), either put the string in quotes or
+escape the individual characters.
+
+```/path/to/ffmpeg -i "%f" -vcodec libx264 -acodec copy "/path/with white space/%b"```
+
index 245ebdbb2ea4b0deb1990ad43eb3c8e2fe26a545..256b6cd67a87e6015f56250351ae199b3532c34b 100644 (file)
@@ -791,6 +791,13 @@ dvr_config_class_pathname_set(void *o, const void *v)
   return 0;
 }
 
+static char *
+dvr_config_prop_pathname_doc(const struct property *p, const char *lang)
+{
+  extern const char *tvh_doc_postprocessor_property[];
+  return prop_md_doc(tvh_doc_postprocessor_property, lang);
+}
+
 extern const char *tvh_doc_dvrconfig_class[];
 
 const idclass_t dvr_config_class = {
@@ -897,7 +904,7 @@ const idclass_t dvr_config_class = {
       .off      = offsetof(dvr_config_t, dvr_retention_days),
       .def.u32  = DVR_RET_ONREMOVE,
       .list     = dvr_config_class_retention_list,
-      .opts     = PO_EXPERT,
+      .opts     = PO_EXPERT | PO_DOC_NLIST,
       .group    = 1,
     },
     {
@@ -908,6 +915,7 @@ const idclass_t dvr_config_class = {
       .off      = offsetof(dvr_config_t, dvr_removal_days),
       .def.u32  = DVR_RET_FOREVER,
       .list     = dvr_config_class_removal_list,
+      .opts     = PO_DOC_NLIST,
       .group    = 1,
     },
     {
@@ -958,7 +966,7 @@ const idclass_t dvr_config_class = {
                      "in the channel or DVR entry will be used."),
       .off      = offsetof(dvr_config_t, dvr_extra_time_pre),
       .list     = dvr_config_class_extra_list,
-      .opts     = PO_ADVANCED,
+      .opts     = PO_ADVANCED | PO_DOC_NLIST,
       .group    = 1,
     },
     {
@@ -969,7 +977,7 @@ const idclass_t dvr_config_class = {
                      "stop time."),
       .off      = offsetof(dvr_config_t, dvr_extra_time_post),
       .list     = dvr_config_class_extra_list,
-      .opts     = PO_ADVANCED,
+      .opts     = PO_ADVANCED | PO_DOC_NLIST,
       .group    = 1,
     },
     {
@@ -981,7 +989,7 @@ const idclass_t dvr_config_class = {
       .off      = offsetof(dvr_config_t, dvr_update_window),
       .list     = dvr_config_entry_class_update_window_list,
       .def.u32  = 24*3600,
-      .opts     = PO_EXPERT,
+      .opts     = PO_EXPERT | PO_DOC_NLIST,
       .group    = 1,
     },
     {
@@ -1125,6 +1133,7 @@ const idclass_t dvr_config_class = {
       .desc     = N_("The string allows you to manually specify the "
                      "full path generation using predefined "
                      "modifiers. See Help for full details."),
+      .doc      = dvr_config_prop_pathname_doc,
       .set      = dvr_config_class_pathname_set,
       .off      = offsetof(dvr_config_t, dvr_pathname),
       .opts     = PO_EXPERT,
index 70a83cadde80d62ce596b5854536290c06995970..d0481e3cf39d7ccadf41d312e2e6fbb357a41879 100644 (file)
@@ -231,8 +231,9 @@ void      idnode_read0  (idnode_t *self, htsmsg_t *m, htsmsg_t *list, int optmas
 int       idnode_write0 (idnode_t *self, htsmsg_t *m, int optmask, int dosave);
 void      idnode_save_check (idnode_t *self, int weak);
 
-#define idclass_serialize(idc, lang) idclass_serialize0(idc, NULL, 0, lang)
-#define idnode_serialize(in, lang)   idnode_serialize0(in, NULL, 0, lang)
+#define idclass_serialize(idc, lang)    idclass_serialize0(idc, NULL, 0, lang)
+#define idclass_serializedoc(idc, lang) idclass_serialize0(idc, NULL, PO_DOC, lang)
+#define idnode_serialize(in, lang)      idnode_serialize0(in, NULL, 0, lang)
 #define idnode_load(in, m)     idnode_write0(in, m, PO_NOSAVE, 0)
 #define idnode_save(in, m)     idnode_read0(in, m, NULL, PO_NOSAVE | PO_USERAW, NULL)
 #define idnode_update(in, m)   idnode_write0(in, m, PO_RDONLY | PO_WRONCE, 1)
index 75bb3ca380f1cb7265c16676f9c63f92eb34fc76..484a753dd03e66e2e311e2c22716251f5b5b2953 100644 (file)
@@ -422,6 +422,13 @@ prop_serialize_value
 
   /* Metadata */
   htsmsg_add_str(m, "caption",  tvh_gettext_lang(lang, pl->name));
+  if ((optmask & PO_DOC) && pl->doc) {
+    char *s = pl->doc(pl, lang);
+    if (s) {
+      htsmsg_add_str(m, "doc", s);
+      free(s);
+    }
+  }
   if (pl->desc)
     htsmsg_add_str(m, "description", tvh_gettext_lang(lang, pl->desc));
   if (pl->islist) {
@@ -498,6 +505,8 @@ prop_serialize_value
     htsmsg_add_bool(m, "multiline", 1);
   if (opts & PO_PERSIST)
     htsmsg_add_bool(m, "persistent", 1);
+  if ((optmask & PO_DOC) && (opts & PO_DOC_NLIST))
+    htsmsg_add_bool(m, "doc_nlist", 1);
 
   /* Enum list */
   if (pl->list) {
@@ -567,6 +576,35 @@ prop_serialize
   }
 }
 
+/**
+ *
+ */
+char *
+prop_md_doc(const char **doc, const char *lang)
+{
+  const char *s;
+  char *r = NULL;
+  size_t l = 0;
+
+  for (; *doc; doc++) {
+    if (*doc[0] == '\xff') {
+      s = tvh_gettext_lang(lang, *doc + 1);
+    } else {
+      s = *doc;
+    }
+    if (r == NULL) {
+      r = strdup(s);
+      l = strlen(s);
+    } else {
+      l += strlen(s) + 1;
+      r = realloc(r, l);
+      strcat(r, s);
+    }
+  }
+  return r;
+}
+
+
 /******************************************************************************
  * Editor Configuration
  *
index 86b601cfe14ef7e7e3266524a769471f065b86d5..1bed0fc4b0ccee2f061154afa48ab4c9f63c29b5 100644 (file)
@@ -63,6 +63,8 @@ typedef enum {
 #define PO_LORDER    (1<<15) // Manage order in lists
 #define PO_MULTILINE (1<<16) // Multiline string
 #define PO_PERSIST   (1<<17) // Persistent value (return back on save)
+#define PO_DOC       (1<<18) // Use doc callback instead description if exists
+#define PO_DOC_NLIST (1<<19) // Do not show list in doc
 
 /*
  * min/max/step helpers
@@ -114,6 +116,9 @@ typedef struct property {
   /* Extended options */
   uint32_t    (*get_opts) (void *ptr);
 
+  /* Documentation callback */
+  char       *(*doc) ( const struct property *prop, const char *lang );
+
   /* Notification callback */
   void        (*notify)   (void *ptr, const char *lang);
 
@@ -144,6 +149,9 @@ static inline int64_t prop_intsplit_from_str(const char *s, int64_t intsplit)
   return s64;
 }
 
+char *
+prop_md_doc(const char **md, const char *lang);
+
 #endif /* __TVH_PROP_H__ */
 
 /******************************************************************************
index 4c4b14dfc1362832bbf870f5cbc73ebd64fecdea..add004842f9286824926e196b68b5aa457fbe326 100644 (file)
@@ -140,7 +140,7 @@ http_markdown_class(http_connection_t *hc, const char *clazz)
     return HTTP_STATUS_NOT_FOUND;
   }
   doc = ic->ic_doc;
-  m = idclass_serialize(ic, lang);
+  m = idclass_serializedoc(ic, lang);
   pthread_mutex_unlock(&global_lock);
   s = htsmsg_get_str(m, "caption");
   if (s) {
@@ -186,22 +186,29 @@ http_markdown_class(http_connection_t *hc, const char *clazz)
       md_text(hq, ": ", "  ", s);
       md_nl(hq, 1);
     }
-    e = htsmsg_get_list(n, "enum");
-    if (e) {
-      HTSMSG_FOREACH(f2, e) {
-        x = htsmsg_field_get_map(f2);
-        if (x) {
-          s = htsmsg_get_str(x, "val");
-        } else {
-          s = htsmsg_field_get_string(f2);
-        }
-        if (s) {
-          md_nl(hq, 1);
-          htsbuf_append(hq, "  * ", 4);
-          md_style(hq, "**", s);
+    s = htsmsg_get_str(n, "doc");
+    if (s) {
+      htsbuf_append_str(hq, s);
+      md_nl(hq, 1);
+    }
+    if (!htsmsg_get_bool_or_default(n, "doc_nlist", 0)) {
+      e = htsmsg_get_list(n, "enum");
+      if (e) {
+        HTSMSG_FOREACH(f2, e) {
+          x = htsmsg_field_get_map(f2);
+          if (x) {
+            s = htsmsg_get_str(x, "val");
+          } else {
+            s = htsmsg_field_get_string(f2);
+          }
+          if (s) {
+            md_nl(hq, 1);
+            htsbuf_append(hq, "  * ", 4);
+            md_style(hq, "**", s);
+          }
         }
+        md_nl(hq, 1);
       }
-      md_nl(hq, 1);
     }
   }
   htsmsg_destroy(m);
index 0cf4bc80464c3ffe9f724f245fcbc9efa5d37368..341e861babd5bbe6ea8446fe01cae205b5fe7d58 100644 (file)
@@ -320,7 +320,7 @@ tvheadend.dvr_upcoming = function(panel, index) {
         selected: selected,
         beforeedit: beforeedit,
         help: function() {
-            new tvheadend.help(_('DVR - Upcoming/Current Recordings'), 'dvr_upcoming.html');
+            new tvheadend.mdhelp('class/dvrentry');
         }
     });
 
@@ -461,7 +461,7 @@ tvheadend.dvr_finished = function(panel, index) {
         tbar: [downloadButton, rerecordButton, moveButton],
         selected: selected,
         help: function() {
-            new tvheadend.help(_('DVR - Finished Recordings'), 'dvr_finished.html');
+            new tvheadend.mdhelp('class/dvrentry');
         }
     });
 
@@ -602,7 +602,7 @@ tvheadend.dvr_failed = function(panel, index) {
         tbar: [downloadButton, rerecordButton, moveButton],
         selected: selected,
         help: function() {
-            new tvheadend.help(_('DVR - Failed Recordings'), 'dvr_failed.html');
+            new tvheadend.mdhelp('class/dvrentry');
         }
     });
 
@@ -628,7 +628,7 @@ tvheadend.dvr_settings = function(panel, index) {
         },
         del: true,
         help: function() {
-            new tvheadend.help(_('DVR'), 'config_dvr.html');
+            new tvheadend.mdhelp('class/dvrconfig');
         }
     });
 
@@ -696,7 +696,7 @@ tvheadend.autorec_editor = function(panel, index) {
           direction: 'ASC'
         },
         help: function() {
-            new tvheadend.help(_('DVR Autorec'), 'dvr_autorec.html');
+            new tvheadend.mdhelp('class/dvrautorec');
         }
     });
 
@@ -749,7 +749,7 @@ tvheadend.timerec_editor = function(panel, index) {
           direction: 'ASC'
         },
         help: function() {
-            new tvheadend.help(_('DVR Timers'), 'dvr_timerec.html');
+            new tvheadend.mdhelp('class/dvrtimerec');
         }
     });