WebUI: Add programme duration filtering

This commit is contained in:
Ian 2014-06-04 18:36:01 +01:00
parent 0c9e9b2461
commit 423617a426
7 changed files with 203 additions and 60 deletions

View file

@ -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

View file

@ -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)

View file

@ -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);
/* ************************************************************************

View file

@ -57,6 +57,8 @@
#include <sys/statvfs.h>
#include "settings.h"
#include <sys/time.h>
//IH
#include <limits.h>
/* **************************************************************************
* 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();

View file

@ -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);

View file

@ -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;

View file

@ -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() {
: "<i>Don't care</i>";
var tag = epgStore.baseParams.tag ? epgStore.baseParams.tag
: "<i>Don't care</i>";
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
//
: "<i>Don't care</i>";
//IH
var minduration = epgStore.baseParams.mindurationstring ? epgStore.baseParams.mindurationstring
: "<i>Don't care</i>";
var maxduration = epgStore.baseParams.maxdurationstring ? epgStore.baseParams.maxdurationstring
: "<i>Don't care</i>";
//
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: ' + '<br><br>'
//IH - correct typo
+ 'to record that match this query: ' + '<br><br>'
+ '<div class="x-smallhdr">Title:</div>' + title + '<br>'
+ '<div class="x-smallhdr">Channel:</div>' + channel + '<br>'
+ '<div class="x-smallhdr">Tag:</div>' + tag + '<br>'
+ '<div class="x-smallhdr">Genre:</div>' + contenttype + '<br>'
+ '<br>' + 'Currently this will match (and record) '
//IH
+ '<div class="x-smallhdr">Min duration:</div>' + minduration + '<br>'
+ '<div class="x-smallhdr">Max duration:</div>' + maxduration + '<br>'
//
+ '<br><br>' + '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;
};
}