#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)
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[] = {
{ "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 },
};
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) */
#include "tvheadend.h"
#include "settings.h"
+#include "string_list.h"
#include "dvr.h"
#include "epg.h"
#include "htsp_server.h"
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 &&
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;
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)
{
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)"),
.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",
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
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;
+}
/// 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
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 ?
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 },
},
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',