diff --git a/src/api/api_epg.c b/src/api/api_epg.c index 5db682b3..814dc543 100644 --- a/src/api/api_epg.c +++ b/src/api/api_epg.c @@ -130,7 +130,10 @@ api_epg_grid const char *ch, *tag, *title, *lang/*, *genre*/; uint32_t start, limit, end; htsmsg_t *l = NULL, *e; - +//IH + int min_duration; + int max_duration; + *resp = htsmsg_create_map(); /* Query params */ @@ -141,13 +144,19 @@ api_epg_grid lang = htsmsg_get_str(args, "lang"); // TODO: support multiple tag/genre/channel? +//IH + min_duration = htsmsg_get_u32_or_default(args, "minduration", 0); + max_duration = htsmsg_get_u32_or_default(args, "maxduration", INT_MAX); + /* Pagination settings */ start = htsmsg_get_u32_or_default(args, "start", 0); limit = htsmsg_get_u32_or_default(args, "limit", 50); /* Query the EPG */ pthread_mutex_lock(&global_lock); - epg_query(&eqr, ch, tag, NULL, /*genre,*/ title, lang); +//IH +// epg_query(&eqr, ch, tag, NULL, /*genre,*/ title, lang); + epg_query(&eqr, ch, tag, NULL, /*genre,*/ title, lang, min_duration, max_duration); epg_query_sort(&eqr); // TODO: optional sorting diff --git a/src/epg.c b/src/epg.c index 03322ccd..484de482 100644 --- a/src/epg.c +++ b/src/epg.c @@ -2205,16 +2205,25 @@ htsmsg_t *epg_genres_list_all ( int major_only, int major_prefix ) static void _eqr_add ( epg_query_result_t *eqr, epg_broadcast_t *e, - epg_genre_t *genre, regex_t *preg, time_t start, const char *lang ) +//IH +// epg_genre_t *genre, regex_t *preg, time_t start, const char *lang ) + epg_genre_t *genre, regex_t *preg, time_t start, const char *lang, int min_duration, int max_duration ) { const char *title; + int duration; /* Ignore */ if ( e->stop < start ) return; if ( !(title = epg_episode_get_title(e->episode, lang)) ) return; if ( genre && !epg_genre_list_contains(&e->episode->genre, genre, 1) ) return; if ( preg && regexec(preg, title, 0, NULL, 0)) return; - + + //IH + duration = (int)e->stop - (int)e->start; + if ( duration < min_duration || duration > max_duration) return; + tvhlog(LOG_INFO, "_eqr_add", "episode duration %d vs min_duration %d and max_duration %d", duration, min_duration, max_duration); +// + /* More space */ if ( eqr->eqr_entries == eqr->eqr_alloced ) { eqr->eqr_alloced = MAX(100, eqr->eqr_alloced * 2); @@ -2228,17 +2237,23 @@ static void _eqr_add static void _eqr_add_channel ( epg_query_result_t *eqr, channel_t *ch, epg_genre_t *genre, - regex_t *preg, time_t start, const char *lang ) +//IH +// regex_t *preg, time_t start, const char *lang ) + regex_t *preg, time_t start, const char *lang, int min_duration, int max_duration ) { epg_broadcast_t *ebc; RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) { - if ( ebc->episode ) _eqr_add(eqr, ebc, genre, preg, start, lang); +//IH +// if ( ebc->episode ) _eqr_add(eqr, ebc, genre, preg, start, lang); + if ( ebc->episode ) _eqr_add(eqr, ebc, genre, preg, start, lang, min_duration, max_duration); } } void epg_query0 ( epg_query_result_t *eqr, channel_t *channel, channel_tag_t *tag, - epg_genre_t *genre, const char *title, const char *lang ) +//IH +// epg_genre_t *genre, const char *title, const char *lang ) + epg_genre_t *genre, const char *title, const char *lang, int min_duration, int max_duration ) { time_t now; channel_tag_mapping_t *ctm; @@ -2259,19 +2274,25 @@ void epg_query0 /* Single channel */ if (channel && !tag) { - _eqr_add_channel(eqr, channel, genre, preg, now, lang); +//IH +// _eqr_add_channel(eqr, channel, genre, preg, now, lang); + _eqr_add_channel(eqr, channel, genre, preg, now, lang, min_duration, max_duration); /* Tag based */ } else if ( tag ) { LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) { if(channel == NULL || ctm->ctm_channel == channel) - _eqr_add_channel(eqr, ctm->ctm_channel, genre, preg, now, lang); +//IH +// _eqr_add_channel(eqr, ctm->ctm_channel, genre, preg, now, lang); + _eqr_add_channel(eqr, ctm->ctm_channel, genre, preg, now, lang, min_duration, max_duration); } /* All channels */ } else { CHANNEL_FOREACH(channel) - _eqr_add_channel(eqr, channel, genre, preg, now, lang); +//IH +// _eqr_add_channel(eqr, channel, genre, preg, now, lang); + _eqr_add_channel(eqr, channel, genre, preg, now, lang, min_duration, max_duration); } if (preg) regfree(preg); @@ -2279,11 +2300,14 @@ void epg_query0 } void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, - epg_genre_t *genre, const char *title, const char *lang) +//IH +// epg_genre_t *genre, const char *title, const char *lang) + epg_genre_t *genre, const char *title, const char *lang, int min_duration, int max_duration) { channel_t *ch = channel ? channel_find(channel) : NULL; channel_tag_t *ct = tag ? channel_tag_find_by_name(tag, 0) : NULL; - epg_query0(eqr, ch, ct, genre, title, lang); + + epg_query0(eqr, ch, ct, genre, title, lang, min_duration, max_duration); } void epg_query_free(epg_query_result_t *eqr) diff --git a/src/epg.h b/src/epg.h index 0f177492..ccee2e96 100644 --- a/src/epg.h +++ b/src/epg.h @@ -544,11 +544,19 @@ void epg_query_free(epg_query_result_t *eqr); void epg_query_sort(epg_query_result_t *eqr); /* Query routines */ +//IH +/* void epg_query0(epg_query_result_t *eqr, struct channel *ch, struct channel_tag *ct, epg_genre_t *genre, const char *title, const char *lang); void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, epg_genre_t *genre, const char *title, const char *lang); +*/ +void epg_query0(epg_query_result_t *eqr, struct channel *ch, + struct channel_tag *ct, epg_genre_t *genre, const char *title, + const char *lang, int min_duration, int max_duration); +void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, + epg_genre_t *genre, const char *title, const char *lang, int min_duration, int max_duration); /* ************************************************************************ diff --git a/src/htsp_server.c b/src/htsp_server.c index f9568541..5d9d71db 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -57,6 +57,8 @@ #include #include "settings.h" #include +//IH +#include /* ************************************************************************** * Datatypes and variables @@ -1059,7 +1061,10 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in) epg_query_result_t eqr; epg_genre_t genre, *eg = NULL; const char *lang; - + //IH + int min_duration; + int max_duration; + /* Required */ if( (query = htsmsg_get_str(in, "query")) == NULL ) return htsp_error("Missing argument 'query'"); @@ -1079,12 +1084,20 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in) lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language; full = htsmsg_get_u32_or_default(in, "full", 0); +//IH + min_duration = htsmsg_get_u32_or_default(in, "minduration", 0); + max_duration = htsmsg_get_u32_or_default(in, "maxduration", INT_MAX); + tvhlog(LOG_INFO, "htsp_server", "min_duration %d and max_duration %d", min_duration, max_duration); +// + /* Check access */ if (!htsp_user_access_channel(htsp, ch)) return htsp_error("User does not have access"); //do the query - epg_query0(&eqr, ch, ct, eg, query, lang); +//IH +// epg_query0(&eqr, ch, ct, eg, query, lang); + epg_query0(&eqr, ch, ct, eg, query, lang, min_duration, max_duration); // create reply out = htsmsg_create_map(); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index b76a43ef..f982a00a 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -758,10 +758,27 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque) const char *tag = http_arg_get(&hc->hc_req_args, "tag"); const char *title = http_arg_get(&hc->hc_req_args, "title"); const char *lang = http_arg_get(&hc->hc_args, "Accept-Language"); + + //IH + int min_duration; + int max_duration; +// if(channel && !channel[0]) channel = NULL; if(tag && !tag[0]) tag = NULL; +//IH + if((s = http_arg_get(&hc->hc_req_args, "minduration")) != NULL) + min_duration = atoi(s); + else + min_duration = 0; + + if((s = http_arg_get(&hc->hc_req_args, "maxduration")) != NULL) + max_duration = atoi(s); + else + max_duration = INT_MAX; +// + if((s = http_arg_get(&hc->hc_req_args, "start")) != NULL) start = atoi(s); @@ -780,7 +797,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque) pthread_mutex_lock(&global_lock); - epg_query(&eqr, channel, tag, eg, title, lang); + epg_query(&eqr, channel, tag, eg, title, lang, min_duration, max_duration); epg_query_sort(&eqr); diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index cfb12c5b..bd4a975e 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -87,8 +87,11 @@ page_simple(http_connection_t *hc, if(s != NULL) { - - epg_query(&eqr, NULL, NULL, NULL, s, lang); + +//IH +// epg_query(&eqr, NULL, NULL, NULL, s, lang); +//IH Force all programme durations for this interface (0 to INT_MAX seconds) + epg_query(&eqr, NULL, NULL, NULL, s, lang, 0, INT_MAX); epg_query_sort(&eqr); c = eqr.eqr_entries; diff --git a/src/webui/static/app/epg.js b/src/webui/static/app/epg.js index f6ee70a4..57eca0c9 100644 --- a/src/webui/static/app/epg.js +++ b/src/webui/static/app/epg.js @@ -332,12 +332,6 @@ tvheadend.epg = function() { header: "Duration", dataIndex: 'duration', renderer: renderDuration -//IH - }, { - width: 100, - id: 'duration', - header: "Duration", - dataIndex: 'duration' }, { width: 250, id: 'channel', @@ -414,36 +408,74 @@ tvheadend.epg = function() { emptyText: 'Filter content type...' }); -//IH - var epgFilterDuration = new Ext.form.ComboBox({ - loadingText: 'Loading...', - width: 150, - displayField: 'label', - store: new Ext.data.ArrayStore({ - fields: ['label','value'], - data: [['Short (< 30m)', 30],['Medium (30m - 90m)', 60],['Long (> 90m)', 21600]] - }), - mode: 'local', - editable: true, - forceSelection: true, - triggerAction: 'all', - emptyText: 'Filter duration...' + //IH + // Slider for duration selection, including tooltip function to display the appropriate string + + var tip = new Ext.slider.Tip({ + getText: function(thumb){ + return thumb.slider.durations[thumb.value]; + } }); + var durationSlider = new Ext.slider.MultiSlider({ + width: 100, + values: [0, 7], + minValue: 0, + maxValue: 7, + increment: 1, + durations: { + 0: '0 min', + 1: '20 min', + 2: '45 min', + 3: '90 min', + 4: '3 hrs', + 5: '6 hrs', + 6: '12 hrs', + 7: '24 hrs' + }, + seconds: { + 0: 0, + 1: 1200, + 2: 2700, + 3: 5400, + 4: 10800, + 5: 21600, + 6: 43200, + 7: 86400 + }, + plugins: tip, + listeners: { + change: function(slider, thumb, value){ +// setduration(slider, thumb, value); + setduration(slider); + }} + }); +// + function epgQueryClear() { delete epgStore.baseParams.channel; delete epgStore.baseParams.tag; delete epgStore.baseParams.contenttype; +//IH + delete epgStore.baseParams.contenttypestring; +// delete epgStore.baseParams.title; //IH - delete epgStore.baseParams.duration; + delete epgStore.baseParams.minduration; + delete epgStore.baseParams.mindurationstring; + delete epgStore.baseParams.maxduration; + delete epgStore.baseParams.maxdurationstring; +// epgFilterChannels.setValue(""); epgFilterChannelTags.setValue(""); epgFilterContentGroup.setValue(""); epgFilterTitle.setValue(""); + //IH - epgFilterDuration.setValue(""); + durationSlider.setValue(0,0); + durationSlider.setValue(1,7); +// epgStore.reload(); } @@ -465,19 +497,34 @@ tvheadend.epg = function() { epgFilterContentGroup.on('select', function(c, r) { if (epgStore.baseParams.contenttype !== r.data.code) { epgStore.baseParams.contenttype = r.data.code; +//IH + epgStore.baseParams.contenttypestring = tvheadend.contentGroupLookupName(r.data.code); + console.log('contentype is',epgStore.baseParams.contenttype,'contenttypestring is',epgStore.baseParams.contenttypestring); +// epgStore.reload(); } }); //IH ------------------ - epgFilterDuration.on('select', function(c, r) { - if (epgStore.baseParams.duration !== r.data.value) { - console.log('Duration filter triggered with value',r.data.value); - console.log('epgStore.baseParams.duration was ',epgStore.baseParams.duration); - epgStore.baseParams.duration = r.data.value; - console.log('epgStore.baseParams.duration is ',epgStore.baseParams.duration); - } - }); +// setduration = function(slider, thumb, value) { + setduration = function(slider) { + + console.log('durationSlider fired'); + + var min = slider.getValue(0); + var max = slider.getValue(1); + + console.log('Min:', min, "Duration:", slider.durations[min], slider.seconds[min]); + console.log('Max:', max, "Duration:", slider.durations[max], slider.seconds[max]); + + epgStore.baseParams.minduration = slider.seconds[min]; + epgStore.baseParams.mindurationstring = slider.durations[min]; + epgStore.baseParams.maxduration = slider.seconds[max]; + epgStore.baseParams.maxdurationstring = slider.durations[max]; + + epgStore.reload(); + }; +// epgFilterTitle.on('valid', function(c) { var value = c.getValue(); @@ -519,8 +566,10 @@ tvheadend.epg = function() { epgFilterContentGroup, '-', //IH - epgFilterDuration, - '-', + 'Duration (test env): 0h ', + durationSlider, + '24h','-', +// { text: 'Reset', handler: epgQueryClear @@ -567,26 +616,46 @@ tvheadend.epg = function() { : "Don't care"; var tag = epgStore.baseParams.tag ? epgStore.baseParams.tag : "Don't care"; - var contenttype = epgStore.baseParams.contenttype ? epgStore.baseParams.contenttype +//IH - correct to display content type as a string as opposed to the underlying (lookup) code +// var contenttype = epgStore.baseParams.contenttype ? epgStore.baseParams.contenttype + var contenttype = epgStore.baseParams.contenttypestring ? epgStore.baseParams.contenttypestring +// : "Don't care"; +//IH + var minduration = epgStore.baseParams.mindurationstring ? epgStore.baseParams.mindurationstring + : "Don't care"; + var maxduration = epgStore.baseParams.maxdurationstring ? epgStore.baseParams.maxdurationstring + : "Don't care"; +// - Ext.MessageBox.confirm('Auto Recorder', - 'This will create an automatic rule that ' + Ext.MessageBox.confirm('Auto Recorder', 'This will create an automatic rule that ' + 'continuously scans the EPG for programmes ' - + 'to record that matches this query: ' + '

' +//IH - correct typo + + 'to record that match this query: ' + '

' + '
Title:
' + title + '
' + '
Channel:
' + channel + '
' + '
Tag:
' + tag + '
' + '
Genre:
' + contenttype + '
' - + '
' + 'Currently this will match (and record) ' +//IH + + '
Min duration:
' + minduration + '
' + + '
Max duration:
' + maxduration + '
' +// + + '

' + 'Currently this will match (and record) ' + epgStore.getTotalCount() + ' events. ' + 'Are you sure?', - function(button) { - if (button === 'no') - return; - createAutoRec2(epgStore.baseParams); - }); + function(button) { + if (button === 'no') + return; + createAutoRec2(epgStore.baseParams); + }); } +// +// IH: TO DO +// +// Check that contenttype is still passed correctly (epgStore.baseParams.contenttype) to autorec vs the new contenttypestring +// Add min/max to autorec rules (C) +// Check they're displayed (js) +// function createAutoRec2(params) { /* Really do it */ params.op = 'createAutoRec'; @@ -597,4 +666,4 @@ tvheadend.epg = function() { } return panel; -}; +}