]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
dvr: Allow selecting (xmltv) category in autorec. (#4665)
authorazlm8t <31170571+azlm8t@users.noreply.github.com>
Fri, 22 Sep 2017 00:09:20 +0000 (01:09 +0100)
committerJaroslav Kysela <perex@perex.cz>
Mon, 16 Oct 2017 16:11:08 +0000 (18:11 +0200)
The xmltv import supports categories such as "movie",
"animated", "biography", so allow autorec to record via these
categories.

We do this by providing three drop-down selectors in the
advanced settings of the autorec. This allows the user
to easily discover the categories available whilst
providing enough capability for reasonably advanced
recordings when coupled with the existing fulltext search.

Issue: #4665

src/api/api_channel.c
src/dvr/dvr.h
src/dvr/dvr_autorec.c
src/epg.h
src/string_list.c
src/string_list.h
src/webui/static/app/dvr.js

index 0f09d7130dfb2487bb09beaadfee1151afb25fb0..4183eb8fb1927ca5492b4769aaaa0f7dcc444bfc 100644 (file)
@@ -24,6 +24,7 @@
 #include "channels.h"
 #include "access.h"
 #include "api.h"
+#include "string_list.h"
 
 static int
 api_channel_is_all(access_t *perm, htsmsg_t *args)
@@ -152,6 +153,51 @@ api_channel_tag_create
   return 0;
 }
 
+static int
+api_channel_cat_list
+  ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+  channel_t *ch;
+  int cfg = api_channel_is_all(perm, args);
+
+  htsmsg_t *l = htsmsg_create_list();
+  string_list_t *sl = string_list_create();
+  const string_list_item_t *item;
+
+  pthread_mutex_lock(&global_lock);
+  /* Build string_list of all categories the user is allowed
+   * to see.
+   */
+  CHANNEL_FOREACH(ch) {
+    if (!cfg && !channel_access(ch, perm, 0)) continue;
+    if (!ch->ch_enabled) continue;
+    epg_broadcast_t *e;
+    RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
+      if (e->category) {
+        RB_FOREACH(item, e->category, h_link) {
+          const char *id = item->id;
+          /* Get rid of duplicates */
+          string_list_insert(sl, id);
+        }
+      }
+    }
+  }
+  pthread_mutex_unlock(&global_lock);
+
+  /* Now we have the unique list, convert it for GUI. */
+  RB_FOREACH(item, sl, h_link) {
+    const char *id = item->id;
+    htsmsg_add_msg(l, NULL, htsmsg_create_key_val(id, id));
+  }
+
+  *resp = htsmsg_create_map();
+  htsmsg_add_msg(*resp, "entries", l);
+
+  string_list_destroy(sl);
+  return 0;
+}
+
+
 void api_channel_init ( void )
 {
   static api_hook_t ah[] = {
@@ -165,6 +211,7 @@ void api_channel_init ( void )
     { "channeltag/list", ACCESS_ANONYMOUS, api_channel_tag_list, NULL },
     { "channeltag/create",  ACCESS_ADMIN,  api_channel_tag_create, NULL },
 
+    { "channelcategory/list",  ACCESS_ANONYMOUS, api_channel_cat_list, NULL },
     { NULL },
   };
 
index f6dd2d0803605841c371bcf7f48d509eb735e0b4..603e2b7de8360639daeb1672bc45784d02f334f3 100644 (file)
@@ -347,6 +347,13 @@ typedef struct dvr_autorec_entry {
   int dae_fulltext;
   
   uint32_t dae_content_type;
+  /* These categories (mainly from xmltv) such as Cooking, Dog racing, Movie.
+   * This allows user to easily do filtering such as '"Movie" "Martial arts"'
+   * or '"Children" "Animated" "Movie"'
+   */
+  char *dae_cat1;                 /** Simple single category from drop-down selection boxes */
+  char *dae_cat2;                 /** Simple single category from drop-down selection boxes */
+  char *dae_cat3;                 /** Simple single category from drop-down selection boxes */
 
   int dae_start;        /* Minutes from midnight */
   int dae_start_window; /* Minutes (duration) */
index cc01ff2cf42c81074af41ee91ff8744b59ed144c..726aa9fdb80cb12269e99edf81baf726b00e06bb 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "tvheadend.h"
 #include "settings.h"
+#include "string_list.h"
 #include "dvr.h"
 #include "epg.h"
 #include "htsp_server.h"
@@ -159,6 +160,9 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
      dae->dae_content_type == 0 &&
      (dae->dae_title == NULL ||
      dae->dae_title[0] == '\0') &&
+     (dae->dae_cat1 == NULL || *dae->dae_cat1 == 0) &&
+     (dae->dae_cat2 == NULL || *dae->dae_cat2 == 0) &&
+     (dae->dae_cat3 == NULL || *dae->dae_cat3 == 0) &&
      dae->dae_brand == NULL &&
      dae->dae_season == NULL &&
      dae->dae_minduration <= 0 &&
@@ -211,6 +215,15 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
       return 0;
   }
 
+  if (e->category) {
+    if (dae->dae_cat1 && *dae->dae_cat1 && !string_list_contains_string(e->category, dae->dae_cat1))
+      return 0;
+    if (dae->dae_cat2 && *dae->dae_cat2 && !string_list_contains_string(e->category, dae->dae_cat2))
+      return 0;
+    if (dae->dae_cat3 && *dae->dae_cat3 && !string_list_contains_string(e->category, dae->dae_cat3))
+      return 0;
+  }
+
   if(dae->dae_start >= 0 && dae->dae_start_window >= 0 &&
      dae->dae_start < 24*60 && dae->dae_start_window < 24*60) {
     struct tm a_time, ev_time;
@@ -986,6 +999,16 @@ dvr_autorec_entry_class_btype_list ( void *o, const char *lang )
   return strtab2htsmsg(tab, 1, lang);
 }
 
+static htsmsg_t *
+dvr_autorec_entry_category_list ( void *o, const char *lang )
+{
+  htsmsg_t *m = htsmsg_create_map();
+  htsmsg_add_str(m, "type",  "api");
+  htsmsg_add_str(m, "uri",   "channelcategory/list");
+  return m;
+}
+
+
 static uint32_t
 dvr_autorec_entry_class_owner_opts(void *o, uint32_t opts)
 {
@@ -999,6 +1022,28 @@ dvr_autorec_entry_class_owner_opts(void *o, uint32_t opts)
 CLASS_DOC(dvrautorec)
 PROP_DOC(duplicate_handling)
 
+/* We provide several category drop-downs to make it easy for user
+ * to select several. So abstract the properties away since they
+ * are nearly identical for each entry.
+ */
+#define CATEGORY_SELECTION_PROP(NUM)                                  \
+  .type     = PT_STR,                                                 \
+  .id       = "cat" #NUM,                                             \
+  .name     = N_("Category " #NUM  " (selection)"),                   \
+  .desc     = N_("The category of the program to look for. The xmltv "\
+                 "providers often supply detailed categories such as "\
+                 "Sitcom, Movie, Track/field, etc. "                  \
+                 "This let you select from categories for current programmes. " \
+                 "It is then combined (AND) with other fields to limit to " \
+                 "programmes that match all categories. "             \
+                 "If this selection list is empty then it means your provider does not " \
+                 "supply programme categories."                       \
+                 ),                                                   \
+  .off      = offsetof(dvr_autorec_entry_t, dae_cat ## NUM),          \
+  .opts     = PO_EXPERT,                                              \
+  .list     = dvr_autorec_entry_category_list
+
+
 const idclass_t dvr_autorec_entry_class = {
   .ic_class      = "dvrautorec",
   .ic_caption    = N_("DVR - Auto-recording (Autorecs)"),
@@ -1046,6 +1091,21 @@ const idclass_t dvr_autorec_entry_class = {
       .set      = dvr_autorec_entry_class_title_set,
       .off      = offsetof(dvr_autorec_entry_t, dae_title),
     },
+    /* We provide a small number of selection drop-downs. This is to
+     * make it easier for users to see what categories are available and
+     * make it easy to record for example '"Movie" "Martial arts" "Western"'
+     * without user needing a regex, and allowing them to easily see what
+     * categories they have available.
+     */
+    {
+      CATEGORY_SELECTION_PROP(1)
+    },
+    {
+      CATEGORY_SELECTION_PROP(2)
+    },
+    {
+      CATEGORY_SELECTION_PROP(3)
+    },
     {
       .type     = PT_BOOL,
       .id       = "fulltext",
index e775f096491fa912b49bce3ce599b239b6836b3d..8fa15a9baec9a91522346f51253566fbeb09bc22 100644 (file)
--- a/src/epg.h
+++ b/src/epg.h
@@ -527,7 +527,7 @@ struct epg_broadcast
   lang_str_t                *credits_cached;   ///< Comma separated cast (for regex searching in GUI/autorec). Kept in sync with cast_map
   struct string_list        *category;         ///< Extra categories (typically from xmltv) such as "Western" or "Sumo Wrestling".
                                                ///< These extra categories are often a superset of our EN 300 468 DVB genre.
-                                               ///< Currently not explicitly searchable in GUI.
+                                               ///< Used with drop-down lists in the GUI.
   struct string_list        *keyword;          ///< Extra keywords (typically from xmltv) such as "Wild West" or "Unicorn".
   lang_str_t                *keyword_cached;   ///< Cached CSV version for regex searches.
   RB_ENTRY(epg_broadcast)    sched_link;       ///< Schedule link
index 09d3ff441722f61c6ef2771793309b1df884f57b..e87ad0d5a9a0c58c63016fa649796c6ec232b2fa 100644 (file)
@@ -172,3 +172,16 @@ string_list_copy(const string_list_t *src)
 
   return ret;
 }
+
+int
+string_list_contains_string(const string_list_t *src, const char *find)
+{
+  string_list_item_t skel;
+  skel.id = (char*)find;
+
+  string_list_item_t *item = RB_FIND(src, &skel, h_link, string_list_item_cmp);
+  /* Can't just return item due to compiler settings preventing ptr to
+   * int conversion
+   */
+  return item != NULL;
+}
index 8657133ad223325f4ab7c1120d1253470984358b..b2205b380581628200cc3ca3d267fb88ec67a2fe 100644 (file)
@@ -72,4 +72,8 @@ int string_list_cmp(const string_list_t *m1, const string_list_t *m2)
 /// Deep clone (shares no pointers, so have to string_list_destroy both.
 string_list_t *string_list_copy(const string_list_t *src)
     __attribute__((warn_unused_result));
+
+/// Searching
+int string_list_contains_string(const string_list_t *src, const char *find);
+
 #endif
index c9838e52161ce45dfc826ffd48d84e81870434f7..20853b48eda33de6cc70d811a05dabe1837d0ebf 100644 (file)
@@ -745,7 +745,7 @@ tvheadend.dvr_settings = function(panel, index) {
 tvheadend.autorec_editor = function(panel, index) {
 
     var list = 'name,title,fulltext,channel,start,start_window,weekdays,' +
-               'record,tag,btype,content_type,minduration,maxduration,' +
+               'record,tag,btype,content_type,cat1,cat2,cat3,minduration,maxduration,' +
                'dedup,directory,config_name,comment';
     var elist = 'enabled,start_extra,stop_extra,' +
                 (tvheadend.accessUpdate.admin ?
@@ -767,6 +767,9 @@ tvheadend.autorec_editor = function(panel, index) {
             tag:          { width: 200 },
             btype:        { width: 50 },
             content_type: { width: 100 },
+            cat1:         { width: 300 },
+            cat2:         { width: 300 },
+            cat3:         { width: 300 },
             minduration:  { width: 100 },
             maxduration:  { width: 100 },
             weekdays:     { width: 160 },
@@ -803,7 +806,7 @@ tvheadend.autorec_editor = function(panel, index) {
         },
         del: true,
         list: 'enabled,name,title,fulltext,channel,tag,start,start_window,' +
-              'weekdays,minduration,maxduration,btype,content_type,' +
+              'weekdays,minduration,maxduration,btype,content_type,cat1,cat2,cat3' +
               'pri,dedup,directory,config_name,owner,creator,comment',
         sort: {
           field: 'name',