diff --git a/configure b/configure index ef718c5247..a5487965a0 100755 --- a/configure +++ b/configure @@ -54,6 +54,7 @@ OPTIONS=( "libmfx_static:yes" "inotify:auto" "epoll:auto" + "pcre:auto" "uriparser:auto" "ccache:auto" "tvhcsa:auto" @@ -384,6 +385,17 @@ if enabled_or_auto satip_client; then enable upnp fi +# +# PCRE +# +if enabled_or_auto pcre; then + if check_pkg libpcre; then + enable pcre + elif enabled pcre; then + die "pcre development support not found (use --disable-pcre)" + fi +fi + # # uriparser # diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index d4880b8806..b9ff99afdf 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -20,6 +20,9 @@ #define DVR_H #include +#if ENABLE_PCRE +#include +#endif #include "epg.h" #include "channels.h" #include "subscriptions.h" @@ -338,7 +341,12 @@ typedef struct dvr_autorec_entry { char *dae_comment; char *dae_title; + int dae_pcre; regex_t dae_title_preg; +#if ENABLE_PCRE + pcre *dae_title_pcre; + pcre_extra *dae_title_pcre_extra; +#endif int dae_fulltext; uint32_t dae_content_type; diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 290ebe004f..0a0c01a4ab 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -36,6 +36,51 @@ struct dvr_autorec_entry_queue autorec_entries; +/* + * + */ +static inline int autorec_regexec(dvr_autorec_entry_t *dae, const char *str) +{ + int r; +#if ENABLE_PCRE + if (dae->dae_pcre) { + int vec[30]; + r = pcre_exec(dae->dae_title_pcre, dae->dae_title_pcre_extra, + str, strlen(str), 0, 0, vec, ARRAY_SIZE(vec)); + return r < 0; + } else +#endif + { + return regexec(&dae->dae_title_preg, str, 0, NULL, 0); + } +} + +/* + * + */ +static void autorec_regfree(dvr_autorec_entry_t *dae) +{ + if (dae->dae_title) { +#if ENABLE_PCRE + if (dae->dae_pcre) { +#ifdef PCRE_CONFIG_JIT + pcre_free_study(dae->dae_title_pcre_extra); +#else + pcre_free(dae->dae_title_pcre_extra); +#endif + pcre_free(dae->dae_title_pcre); + dae->dae_title_pcre_extra = NULL; + dae->dae_title_pcre = NULL; + } else +#endif + { + regfree(&dae->dae_title_preg); + } + free(dae->dae_title); + dae->dae_title = NULL; + } +} + /* * */ @@ -175,33 +220,7 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e) return 0; } - /* Do not check title if the event is from the serieslink group */ - if(dae->dae_serieslink == NULL && - dae->dae_title != NULL && dae->dae_title[0] != '\0') { - lang_str_ele_t *ls; - if (!dae->dae_fulltext) { - if(!e->episode->title) return 0; - RB_FOREACH(ls, e->episode->title, link) - if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break; - } else { - ls = NULL; - if (e->episode->title) - RB_FOREACH(ls, e->episode->title, link) - if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break; - if (!ls && e->episode->subtitle) - RB_FOREACH(ls, e->episode->subtitle, link) - if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break; - if (!ls && e->summary) - RB_FOREACH(ls, e->summary, link) - if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break; - if (!ls && e->description) - RB_FOREACH(ls, e->description, link) - if (!regexec(&dae->dae_title_preg, ls->str, 0, NULL, 0)) break; - } - if (!ls) return 0; - } - - // Note: ignore channel test if we allow quality unlocking + /* Note: ignore channel test if we allow quality unlocking */ if ((cfg = dae->dae_config) == NULL) return 0; if(dae->dae_channel != NULL) { @@ -269,6 +288,33 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e) if(!((1 << ((tm.tm_wday ?: 7) - 1)) & dae->dae_weekdays)) return 0; } + + /* Do not check title if the event is from the serieslink group */ + if(dae->dae_serieslink == NULL && + dae->dae_title != NULL && dae->dae_title[0] != '\0') { + lang_str_ele_t *ls; + if (!dae->dae_fulltext) { + if(!e->episode->title) return 0; + RB_FOREACH(ls, e->episode->title, link) + if (!autorec_regexec(dae, ls->str)) break; + } else { + ls = NULL; + if (e->episode->title) + RB_FOREACH(ls, e->episode->title, link) + if (!autorec_regexec(dae, ls->str)) break; + if (!ls && e->episode->subtitle) + RB_FOREACH(ls, e->episode->subtitle, link) + if (!autorec_regexec(dae, ls->str)) break; + if (!ls && e->summary) + RB_FOREACH(ls, e->summary, link) + if (!autorec_regexec(dae, ls->str)) break; + if (!ls && e->description) + RB_FOREACH(ls, e->description, link) + if (!autorec_regexec(dae, ls->str)) break; + } + if (!ls) return 0; + } + return 1; } @@ -397,10 +443,7 @@ autorec_entry_destroy(dvr_autorec_entry_t *dae, int delconf) free(dae->dae_creator); free(dae->dae_comment); - if(dae->dae_title != NULL) { - free(dae->dae_title); - regfree(&dae->dae_title_preg); - } + autorec_regfree(dae); if(dae->dae_channel != NULL) LIST_REMOVE(dae, dae_channel_link); @@ -526,15 +569,52 @@ dvr_autorec_entry_class_title_set(void *o, const void *v) dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)o; const char *title = v ?: ""; if (strcmp(title, dae->dae_title ?: "")) { - if (dae->dae_title) { - regfree(&dae->dae_title_preg); - free(dae->dae_title); - dae->dae_title = NULL; + if (dae->dae_title) + autorec_regfree(dae); + if (title[0] != '\0') { +#if ENABLE_PCRE + if (dae->dae_pcre) { + const char *estr; + int eoff; + dae->dae_title_pcre = pcre_compile(title, PCRE_CASELESS | PCRE_UTF8, + &estr, &eoff, NULL); + if (dae->dae_title_pcre == NULL) { + tvherror(LS_DVR, "Unable to compile PCRE '%s': %s", title, estr); + } else { + dae->dae_title_pcre_extra = pcre_study(dae->dae_title_pcre, + PCRE_STUDY_JIT_COMPILE, &estr); + if (dae->dae_title_pcre_extra == NULL && estr) + tvherror(LS_DVR, "Unable to study PCRE '%s': %s", title, estr); + else + dae->dae_title = strdup(title); + } + } else +#endif + { + if (!regcomp(&dae->dae_title_preg, title, + REG_ICASE | REG_EXTENDED | REG_NOSUB)) + dae->dae_title = strdup(title); + else + tvherror(LS_DVR, "Unable to compile regex '%s'", title); + } } - if (title[0] != '\0' && - !regcomp(&dae->dae_title_preg, title, - REG_ICASE | REG_EXTENDED | REG_NOSUB)) - dae->dae_title = strdup(title); + return 1; + } + return 0; +} + +static int +dvr_autorec_entry_class_pcre_set(void *o, const void *v) +{ + dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)o; + int pcre = v ? *(int *)v : 0; + char *title; + if (dae->dae_pcre != pcre) { + title = dae->dae_title ? strdup(dae->dae_title) : NULL; + autorec_regfree(dae); + dae->dae_pcre = pcre; + dvr_autorec_entry_class_title_set(o, title); + free(title); return 1; } return 0; @@ -1048,6 +1128,15 @@ const idclass_t dvr_autorec_entry_class = { "matched against title, subtitle, summary and description."), .off = offsetof(dvr_autorec_entry_t, dae_fulltext), }, + { + .type = PT_BOOL, + .id = "pcre", + .name = N_("PCRE"), + .desc = N_("Use PCRE regular expression library instead posix " + "extended regular expressions."), + .set = dvr_autorec_entry_class_pcre_set, + .off = offsetof(dvr_autorec_entry_t, dae_pcre), + }, { .type = PT_STR, .id = "channel", diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index b4ace35ad8..c48180b7af 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -723,7 +723,7 @@ tvheadend.dvr_settings = function(panel, index) { */ tvheadend.autorec_editor = function(panel, index) { - var list = 'name,title,fulltext,channel,start,start_window,weekdays,' + + var list = 'name,title,fulltext,pcre,channel,start,start_window,weekdays,' + 'record,tag,btype,content_type,minduration,maxduration,' + 'dedup,directory,config_name,comment'; var elist = 'enabled,start_extra,stop_extra,' + @@ -742,6 +742,7 @@ tvheadend.autorec_editor = function(panel, index) { directory: { width: 200 }, title: { width: 300 }, fulltext: { width: 70 }, + pcre: { width: 70 }, channel: { width: 200 }, tag: { width: 200 }, btype: { width: 50 }, @@ -781,7 +782,7 @@ tvheadend.autorec_editor = function(panel, index) { }, }, del: true, - list: 'enabled,name,title,fulltext,channel,tag,start,start_window,' + + list: 'enabled,name,title,fulltext,pcre,channel,tag,start,start_window,' + 'weekdays,minduration,maxduration,btype,content_type,' + 'pri,dedup,directory,config_name,owner,creator,comment', sort: {