diff --git a/src/cron.c b/src/cron.c index 6d7a9887..802cb540 100644 --- a/src/cron.c +++ b/src/cron.c @@ -113,7 +113,7 @@ int cron_set_string ( cron_t *cron, const char *str ) { int save = 0; cron_t tmp; - if (!cron->str || !strcmp(cron->str, str)) { + if (!cron->str || strcmp(cron->str, str)) { tmp.str = (char*)str; if (_cron_parse(&tmp, str)) { if (cron->str) free(cron->str); diff --git a/src/epggrab.c b/src/epggrab.c index 9cb3e609..8576f48c 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -20,7 +20,7 @@ pthread_cond_t epggrab_cond; /* Config */ uint32_t epggrab_advanced; -uint32_t epggrab_eit; +uint32_t epggrab_eitenabled; uint32_t epggrab_interval; epggrab_module_t* epggrab_module; epggrab_sched_list_t epggrab_schedule; @@ -136,10 +136,32 @@ epggrab_module_t* epggrab_module_find_by_id ( const char *id ) htsmsg_t *epggrab_module_list ( void ) { - htsmsg_t *a = htsmsg_create_list(); + epggrab_module_t *m; + htsmsg_t *e, *a = htsmsg_create_list(); + LIST_FOREACH(m, &epggrab_modules, link) { + e = htsmsg_create_map(); + htsmsg_add_str(e, "id", m->id); + if(m->name) htsmsg_add_str(e, "name", m->name); + else printf("no name: %s\n", m->id); + if(m->path) htsmsg_add_str(e, "path", m->path); + htsmsg_add_u32(e, "flags", m->flags); + htsmsg_add_msg(a, NULL, e); + } return a; } +void epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e ) +{ + // TODO: implement this? +} + +htsmsg_t *epggrab_module_grab + ( epggrab_module_t *mod, const char *cmd, const char *opts ) +{ + // TODO: implement this + return NULL; +} + /* ************************************************************************** * Configuration * *************************************************************************/ @@ -162,57 +184,56 @@ static int _update_str ( char **dst, const char *src ) return save; } -static int _epggrab_schedule_deserialize ( htsmsg_t *m ) +static int _epggrab_schedule_deserialize ( htsmsg_t *a ) { int save = 0; - htsmsg_t *a, *e; + htsmsg_t *e; htsmsg_field_t *f; epggrab_sched_t *es; epggrab_module_t *mod; const char *str; - es = LIST_FIRST(&epggrab_schedule); - if ( (a = htsmsg_get_list(m, "schedule")) ) { - HTSMSG_FOREACH(f, a) { - if ((e = htsmsg_get_map_by_field(f))) { - if (es) { - es = LIST_NEXT(es, link); - } else { - es = calloc(1, sizeof(epggrab_sched_t)); - save = 1; - LIST_INSERT_HEAD(&epggrab_schedule, es, link); - } + es = TAILQ_FIRST(&epggrab_schedule); + HTSMSG_FOREACH(f, a) { + if ((e = htsmsg_get_map_by_field(f))) { + if (!es) { + es = calloc(1, sizeof(epggrab_sched_t)); + save = 1; + TAILQ_INSERT_TAIL(&epggrab_schedule, es, link); + } - mod = NULL; - if ((str = htsmsg_get_str(e, "module"))) - mod = epggrab_module_find_by_id(str); - if (es->mod != mod) { - es->mod = mod; - save = 1; - } + mod = NULL; + if ((str = htsmsg_get_str(e, "module"))) + mod = epggrab_module_find_by_id(str); + if (es->mod != mod) { + es->mod = mod; + save = 1; + } - save |= _update_str(&es->cmd, htsmsg_get_str(e, "command")); - save |= _update_str(&es->opts, htsmsg_get_str(e, "options")); - str = htsmsg_get_str(e, "cron"); - if (es->cron) { - if (!str) { - free(es->cron); - es->cron = NULL; - save = 1; - } else { - save |= cron_set_string(es->cron, str); - } - } else if (str) { - es->cron = cron_create(str); + save |= _update_str(&es->cmd, htsmsg_get_str(e, "command")); + save |= _update_str(&es->opts, htsmsg_get_str(e, "options")); + str = htsmsg_get_str(e, "cron"); + if (es->cron) { + if (!str) { + free(es->cron); + es->cron = NULL; save = 1; + } else { + save |= cron_set_string(es->cron, str); } + } else if (str) { + es->cron = cron_create(str); + save = 1; } } + + /* Next */ + if (es) es = TAILQ_NEXT(es, link); } if (es) - while ( LIST_NEXT(es, link) ) { - LIST_REMOVE(es, link); + while ( (es = TAILQ_NEXT(es, link)) ) { + TAILQ_REMOVE(&epggrab_schedule, es, link); save = 1; } @@ -224,7 +245,7 @@ static htsmsg_t *_epggrab_schedule_serialize ( void ) epggrab_sched_t *es; htsmsg_t *e, *a; a = htsmsg_create_list(); - LIST_FOREACH(es, &epggrab_schedule, link) { + TAILQ_FOREACH(es, &epggrab_schedule, link) { e = htsmsg_create_map(); if ( es->mod ) htsmsg_add_str(e, "module", es->mod->id); if ( es->cmd ) htsmsg_add_str(e, "command", es->cmd); @@ -246,7 +267,7 @@ static void _epggrab_load ( void ) /* Load settings */ htsmsg_get_u32(m, "advanced", &epggrab_advanced); - htsmsg_get_u32(m, "eit", &epggrab_eit); + htsmsg_get_u32(m, "eit", &epggrab_eitenabled); htsmsg_get_u32(m, "interval", &epggrab_interval); if ( (str = htsmsg_get_str(m, "module")) ) epggrab_module = epggrab_module_find_by_id(str); @@ -271,6 +292,7 @@ void epggrab_save ( void ) if ( epggrab_module ) htsmsg_add_str(m, "module", epggrab_module->id); htsmsg_add_msg(m, "schedule", _epggrab_schedule_serialize()); + htsmsg_print(m); hts_settings_save(m, "epggrab/config"); htsmsg_destroy(m); } @@ -288,9 +310,9 @@ int epggrab_set_advanced ( uint32_t advanced ) int epggrab_set_eitenabled ( uint32_t eitenabled ) { int save = 0; - if ( epggrab_eit != eitenabled ) { + if ( epggrab_eitenabled != eitenabled ) { save = 1; - eitenabled = eitenabled; + epggrab_eitenabled = eitenabled; } return save; } @@ -340,6 +362,7 @@ htsmsg_t *epggrab_get_schedule ( void ) static void _epggrab_module_run ( epggrab_module_t *mod, const char *icmd, const char *iopts ) { +#if 0 int save = 0; time_t tm1, tm2; htsmsg_t *data; @@ -399,6 +422,7 @@ static void _epggrab_module_run if (cmd) free(cmd); if (opts) free(cmd); pthread_mutex_lock(&epggrab_mutex); +#endif } /* @@ -426,7 +450,7 @@ static time_t _epggrab_thread_advanced ( void ) /* Determine which to run */ time(&ret); - LIST_FOREACH(s, &epggrab_schedule, link) { + TAILQ_FOREACH(s, &epggrab_schedule, link) { if ( cron_is_time(s->cron) ) { _epggrab_module_run(s->mod, s->cmd, s->opts); break; // TODO: can only run once else config may have changed @@ -480,10 +504,11 @@ static void* _epggrab_thread ( void* p ) void epggrab_init ( void ) { /* Defaults */ - epggrab_advanced = 0; - epggrab_eit = 1; // on air grab enabled - epggrab_interval = 12 * 3600; // hours - epggrab_module = NULL; // disabled + epggrab_advanced = 0; + epggrab_eitenabled = 1; // on air grab enabled + epggrab_interval = 12 * 3600; // hours + epggrab_module = NULL; // disabled + TAILQ_INIT(&epggrab_schedule); /* Initialise modules */ eit_init(&epggrab_modules); diff --git a/src/epggrab.h b/src/epggrab.h index 50b9887b..6b704427 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -57,10 +57,11 @@ typedef struct epggrab_module epggrab_module_t; /* * Grabber flags */ -#define EPGGRAB_MODULE_SYNC 0x1 -#define EPGGRAB_MODULE_ASYNC 0x2 -#define EPGGRAB_MODULE_SIMPLE 0x4 -#define EPGGRAB_MODULE_ADVANCED 0x8 +#define EPGGRAB_MODULE_SYNC 0x01 +#define EPGGRAB_MODULE_ASYNC 0x02 +#define EPGGRAB_MODULE_SIMPLE 0x04 +#define EPGGRAB_MODULE_ADVANCED 0x08 +#define EPGGRAB_MODULE_EXTERNAL 0x10 /* * Grabber base class @@ -96,7 +97,7 @@ struct epggrab_module /* * Default module functions */ -void epggrab_module_enable ( epggrab_module_t *m, uint8_t e ); +void epggrab_module_enable_socket ( epggrab_module_t *m, uint8_t e ); htsmsg_t *epggrab_module_grab ( epggrab_module_t *m, const char *cmd, const char *opts ); void epggrab_module_channels_load ( epggrab_module_t *m ); void epggrab_module_channels_save ( epggrab_module_t *m, const char *path ); @@ -129,7 +130,7 @@ htsmsg_t* epggrab_module_list ( void ); */ typedef struct epggrab_sched { - LIST_ENTRY(epggrab_sched) link; + TAILQ_ENTRY(epggrab_sched) link; cron_t *cron; ///< Cron definition epggrab_module_t *mod; ///< Module char *cmd; ///< Command @@ -139,7 +140,7 @@ typedef struct epggrab_sched /* * Schedule list */ -LIST_HEAD(epggrab_sched_list, epggrab_sched); +TAILQ_HEAD(epggrab_sched_list, epggrab_sched); typedef struct epggrab_sched_list epggrab_sched_list_t; /* diff --git a/src/epggrab/pyepg.c b/src/epggrab/pyepg.c index 088d3a56..29cd5b08 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/pyepg.c @@ -122,7 +122,7 @@ static int _pyepg_parse_brand ( htsmsg_t *data, epggrab_stats_t *stats ) } /* Set icon */ -#if TODO +#if TODO_ICON_SUPPORT if ((str = htsmsg_xml_get_cdata_str(tags, "icon"))) { save |= epg_brand_set_icon(brand, str); } @@ -166,7 +166,7 @@ static int _pyepg_parse_season ( htsmsg_t *data, epggrab_stats_t *stats ) } /* Set title */ -#if TODO +#if TODO_EXTRA_METADATA if ((str = htsmsg_xml_get_cdata_str(tags, "title"))) { save |= epg_season_set_title(season, str); } @@ -252,7 +252,7 @@ static int _pyepg_parse_episode ( htsmsg_t *data, epggrab_stats_t *stats ) /* Genre */ // TODO: can actually have multiple! -#if TODO +#if TODO_GENRE_SUPPORT if ((str = htsmsg_xml_get_cdata_str(tags, "genre"))) { // TODO: conversion? save |= epg_episode_set_genre(episode, str); @@ -396,13 +396,15 @@ static int _pyepg_parse void pyepg_init ( epggrab_module_list_t *list ) { - _pyepg_module.id = strdup("pyepg_sync"); - _pyepg_module.path = strdup("/usr/bin/pyepg"); + _pyepg_module.id = strdup("pyepg"); + _pyepg_module.path = strdup("/usr/bin/pyepg"); + _pyepg_module.name = strdup("PyEPG"); *((uint8_t*)&_pyepg_module.flags) = EPGGRAB_MODULE_SYNC | EPGGRAB_MODULE_ASYNC | EPGGRAB_MODULE_ADVANCED - | EPGGRAB_MODULE_SIMPLE; - _pyepg_module.enable = epggrab_module_enable; + | EPGGRAB_MODULE_SIMPLE + | EPGGRAB_MODULE_EXTERNAL; + _pyepg_module.enable = epggrab_module_enable_socket; _pyepg_module.grab = epggrab_module_grab; _pyepg_module.parse = _pyepg_parse; _pyepg_module.channels = &_pyepg_channels; diff --git a/src/epggrab/xmltv.c b/src/epggrab/xmltv.c index dee9f9fc..9be657fd 100644 --- a/src/epggrab/xmltv.c +++ b/src/epggrab/xmltv.c @@ -420,6 +420,8 @@ static void _xmltv_load_grabbers ( epggrab_module_list_t *list ) mod->id = mod->path = strdup(&outbuf[p]); mod->name = malloc(200); sprintf((char*)mod->name, "XMLTV: %s", &outbuf[n]); + *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_SYNC + | EPGGRAB_MODULE_SIMPLE; mod->parse = _xmltv_parse; mod->ch_add = epggrab_module_channel_add; mod->ch_rem = epggrab_module_channel_rem; @@ -446,8 +448,9 @@ void xmltv_init ( epggrab_module_list_t *list ) mod->name = strdup("XMLTV: Advanced"); *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_ASYNC | EPGGRAB_MODULE_SYNC - | EPGGRAB_MODULE_ADVANCED; - mod->enable = epggrab_module_enable; + | EPGGRAB_MODULE_ADVANCED + | EPGGRAB_MODULE_EXTERNAL; + mod->enable = epggrab_module_enable_socket; mod->grab = epggrab_module_grab; mod->parse = _xmltv_parse; mod->channels = &_xmltv_channels; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 7e6c5145..fdf8ccb8 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -42,6 +42,7 @@ #include "epggrab.h" #include "epg.h" #include "iptv_input.h" +#include "epggrab/xmltv.h" static void extjs_load(htsbuf_queue_t *hq, const char *script) @@ -322,11 +323,6 @@ extjs_channels_update(htsmsg_t *in) if((s = htsmsg_get_str(c, "name")) != NULL) channel_rename(ch, s); -#if TODO_XMLTV_REMOVE_THIS - if((s = htsmsg_get_str(c, "xmltvsrc")) != NULL) - channel_set_xmltv_source(ch, xmltv_channel_find_by_displayname(s)); -#endif - if((s = htsmsg_get_str(c, "ch_icon")) != NULL) channel_set_icon(ch, s); @@ -376,11 +372,6 @@ extjs_channels(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_str(c, "name", ch->ch_name); htsmsg_add_u32(c, "chid", ch->ch_id); -#if TODO_XMLTV_REMOVE_THIS - if(ch->ch_xc != NULL) - htsmsg_add_str(c, "xmltvsrc", ch->ch_xc->xc_displayname); -#endif - if(ch->ch_icon != NULL) htsmsg_add_str(c, "ch_icon", ch->ch_icon); @@ -476,16 +467,16 @@ json_single_record(htsmsg_t *rec, const char *root) static int extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) { -#if TODO_EPGGRAB_CONFIG htsbuf_queue_t *hq = &hc->hc_reply; const char *op = http_arg_get(&hc->hc_req_args, "op"); - xmltv_channel_t *xc; - htsmsg_t *out, *array, *e, *r; - const char *s; + htsmsg_t *out, *array, *r; + const char *str; if(op == NULL) return 400; + printf("extjs_epggrab: %s\n", op); + pthread_mutex_lock(&global_lock); if(http_access_verify(hc, ACCESS_ADMIN)) { @@ -495,67 +486,64 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) pthread_mutex_unlock(&global_lock); - if(!strcmp(op, "listChannels")) { + /* Basic settings (not the advanced schedule) */ + if(!strcmp(op, "loadSettings")) { + pthread_mutex_lock(&epggrab_mutex); + r = htsmsg_create_map(); + htsmsg_add_u32(r, "eitenabled", epggrab_eitenabled); + htsmsg_add_u32(r, "advanced", epggrab_advanced); + if (epggrab_module) + htsmsg_add_str(r, "module", epggrab_module->id); + htsmsg_add_u32(r, "interval", epggrab_interval); + pthread_mutex_unlock(&epggrab_mutex); + + out = json_single_record(r, "epggrabSettings"); + + /* List of modules */ + } else if (!strcmp(op, "moduleList")) { out = htsmsg_create_map(); - array = htsmsg_create_list(); - - e = htsmsg_create_map(); - htsmsg_add_str(e, "xcTitle", "None"); - htsmsg_add_msg(array, NULL, e); - - pthread_mutex_lock(&global_lock); - LIST_FOREACH(xc, &xmltv_displaylist, xc_displayname_link) { - e = htsmsg_create_map(); - htsmsg_add_str(e, "xcTitle", xc->xc_displayname); - htsmsg_add_msg(array, NULL, e); - } - pthread_mutex_unlock(&global_lock); - + pthread_mutex_lock(&epggrab_mutex); + array = epggrab_module_list(); + pthread_mutex_unlock(&epggrab_mutex); htsmsg_add_msg(out, "entries", array); - } else if(!strcmp(op, "loadSettings")) { - - pthread_mutex_lock(&xmltv_mutex); - r = htsmsg_create_map(); - - if((s = xmltv_get_current_grabber()) != NULL) - htsmsg_add_str(r, "grabber", s); - - htsmsg_add_u32(r, "grabinterval", xmltv_grab_interval); - htsmsg_add_u32(r, "grabenable", xmltv_grab_enabled); - pthread_mutex_unlock(&xmltv_mutex); - - out = json_single_record(r, "xmltvSettings"); - - } else if(!strcmp(op, "saveSettings")) { - - pthread_mutex_lock(&xmltv_mutex); - - s = http_arg_get(&hc->hc_req_args, "grabber"); - xmltv_set_current_grabber(s); - - s = http_arg_get(&hc->hc_req_args, "grabinterval"); - xmltv_set_grab_interval(atoi(s)); - - s = http_arg_get(&hc->hc_req_args, "grabenable"); - xmltv_set_grab_enable(!!s); - - pthread_mutex_unlock(&xmltv_mutex); + /* Advanced schedule */ + } else if (!strcmp(op, "loadSchedule")) { + out = htsmsg_create_map(); + pthread_mutex_lock(&epggrab_mutex); + array = epggrab_get_schedule(); + pthread_mutex_unlock(&epggrab_mutex); + htsmsg_add_msg(out, "entries", array); + /* Save settings */ + } else if (!strcmp(op, "saveSettings") ) { + int save = 0; + pthread_mutex_lock(&epggrab_mutex); + if ( http_arg_get(&hc->hc_req_args, "advanced") ) + save |= epggrab_set_advanced(1); + else + save |= epggrab_set_advanced(0); + if ( http_arg_get(&hc->hc_req_args, "eitenabled") ) + save |= epggrab_set_eitenabled(1); + else + save |= epggrab_set_eitenabled(0); + if ( (str = http_arg_get(&hc->hc_req_args, "interval")) ) + save |= epggrab_set_interval(atoi(str)); + if ( (str = http_arg_get(&hc->hc_req_args, "module")) ) + save |= epggrab_set_module_by_id(str); + if ( (str = http_arg_get(&hc->hc_req_args, "schedule")) ) { + if ( (array = htsmsg_json_deserialize(str)) ) { + htsmsg_print(array); + save |= epggrab_set_schedule(array); + htsmsg_destroy(array); + } + } + if (save) epggrab_save(); + pthread_mutex_unlock(&epggrab_mutex); out = htsmsg_create_map(); htsmsg_add_u32(out, "success", 1); - } else if(!strcmp(op, "listGrabbers")) { - - out = htsmsg_create_map(); - - pthread_mutex_lock(&xmltv_mutex); - array = xmltv_list_grabbers(); - pthread_mutex_unlock(&xmltv_mutex); - if(array != NULL) - htsmsg_add_msg(out, "entries", array); - } else { return HTTP_STATUS_BAD_REQUEST; } @@ -564,7 +552,6 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) htsmsg_destroy(out); http_output_content(hc, "text/x-json; charset=UTF-8"); -#endif return 0; } diff --git a/src/webui/static/app/epggrab.js b/src/webui/static/app/epggrab.js index b4baa0fc..6f122800 100644 --- a/src/webui/static/app/epggrab.js +++ b/src/webui/static/app/epggrab.js @@ -1,194 +1,422 @@ -tvheadend.grabberStore = new Ext.data.JsonStore({ - root:'entries', - fields: ['identifier','name','version','apiconfig'], - url:'xmltv', - baseParams: { - op: 'listGrabbers' - } -}); - tvheadend.epggrab = function() { + /* **************************************************************** + * Data + * ***************************************************************/ + + /* + * Module lists (I'm sure there is a better way!) + */ + var EPGGRAB_MODULE_SIMPLE = 0x04; + var EPGGRAB_MODULE_ADVANCED = 0x08; + var EPGGRAB_MODULE_EXTERNAL = 0x10; + + var moduleStore = new Ext.data.JsonStore({ + root : 'entries', + url : 'epggrab', + baseParams : { op : 'moduleList' }, + autoLoad : true, + fields : [ 'id', 'name', 'path', 'flags' ] + }); + var simpleModuleStore = new Ext.data.Store({ + recordType: moduleStore.recordType + }); + var advancedModuleStore = new Ext.data.Store({ + recordType: moduleStore.recordType + }); + var externalModuleStore = new Ext.data.Store({ + recordType: moduleStore.recordType + }); + moduleStore.on('load', function() { + moduleStore.filterBy(function(r) { + return r.get('flags') & EPGGRAB_MODULE_SIMPLE; + }); + moduleStore.each(function(r) { + simpleModuleStore.add(r.copy()); + }); + moduleStore.filterBy(function(r) { + return r.get('flags') & (EPGGRAB_MODULE_ADVANCED | EPGGRAB_MODULE_SIMPLE); + }); + moduleStore.each(function(r) { + advancedModuleStore.add(r.copy()); + }); + moduleStore.filterBy(function(r) { + return r.get('flags') & EPGGRAB_MODULE_EXTERNAL; + }); + moduleStore.each(function(r) { + externalModuleStore.add(r.copy()); + }); + }); + + /* + * Schedule + */ + + var scheduleRow = Ext.data.Record.create( + [ + { name : 'module' }, + { name : 'command' }, + { name : 'options' }, + { name : 'cron' } + ] + ); + + var scheduleStore = new Ext.data.JsonStore({ + root : 'entries', + url : 'epggrab', + baseParams : { op : 'loadSchedule' }, + autoLoad : true, + fields : [ 'module', 'command', 'options', 'cron' ] + }); + + /* + * Basic Config + */ + var confreader = new Ext.data.JsonReader( { root: 'epggrabSettings' }, - [ 'module', 'eitenable', 'advanced' ] + [ 'module', 'eitenabled', 'advanced', 'interval' ] ); - var grabberSelect = new Ext.form.ComboBox({ - loadingText: 'Loading, please wait...', - fieldLabel: 'XML-TV Source', - name: 'grabber', - width: 350, - displayField:'name', - valueField:'identifier', - store: tvheadend.grabberStore, - forceSelection: true, - editable: false, - triggerAction: 'all', - mode: 'remote', - emptyText: 'Select grabber' - }); - grabberSelect.setVisible(false); + /* **************************************************************** + * Simple Config + * ***************************************************************/ - var advancedCheck = new Ext.form.Checkbox({ - fieldLabel : 'Advanced Config', - name : 'advanced', - }); - - var moduleSelect = new Ext.form.ComboBox({ - fieldLabel : 'Grab Module', - name : 'module', - width : 150, + /* + * Module selector + */ + var simpleModule = new Ext.form.ComboBox({ + fieldLabel : 'Module', + hiddenName : 'module', + width : 300, + valueField : 'id', + displayField : 'name', + forceSelection : true, + editable : false, + mode : 'local', + triggerAction : 'all', + store : simpleModuleStore + }); + + /* + * Interval selector + */ + var intervalUnits = [ + [ 86400, 'Days' ], + [ 3600, 'Hours' ], + [ 60, 'Minutes' ], + [ 1, 'Seconds' ] + ]; + var intervalValue = new Ext.form.NumberField({ + width : 300, + allowNegative : false, + allowDecimals : false, + minValue : 1, + maxValue : 7, + value : 1, + fieldLabel : 'Grab interval', + name : 'intervalValue', + listeners : { + 'valid' : function (e) { + v = e.getValue() * intervalUnit.getValue(); + interval.setValue(v); + } + } + }) + var intervalUnit = new Ext.form.ComboBox({ + name : 'intervalUnit', + width : 300, valueField : 'key', displayField : 'value', + value : 86400, forceSelection : true, editable : false, triggerAction : 'all', mode : 'local', store : new Ext.data.SimpleStore({ - fields : [ 'key', 'value' ], - data : [ - [ 'xmltv', 'XMLTV' ], - [ 'pyepg', 'PyEPG' ], - ] - }) + fields : [ 'key', 'value' ], + data : intervalUnits + }), + listeners : { + 'change' : function (e, n, o) { + intervalValue.maxValue = (7 * 86400) / n; + intervalValue.validate(); + } + } + }); + var interval = new Ext.form.Hidden({ + name : 'interval', + value : 86400, + listeners : { + 'enable' : function (e) { + v = e.getValue(); + for ( i = 0; i < intervalUnits.length; i++ ) { + u = intervalUnits[i][0]; + if ( (v % u) == 0 ) { + intervalUnit.setValue(u); + intervalValue.maxValue = (7 * 86400) / u; + intervalValue.setValue(v / u); + intervalValue.validate(); + break; + } + } + } + } }); - var confpanel = new Ext.FormPanel( - { - title : 'EPG Grabber', - iconCls : 'xml', - border : false, - bodyStyle : 'padding:15px', - labelAlign : 'right', - labelWidth : 200, - waitMsgTarget : true, - reader : confreader, - layout : 'form', - defaultType : 'textfield', - items : [ - moduleSelect, - new Ext.form.Checkbox({ - fieldLabel : 'Enable EIT', - name : 'eitenable' - }), - advancedCheck, - grabberSelect - ], - tbar: [ - { - tooltip: 'Save changes made to configuration below', - iconCls:'save', - text: "Save configuration", - handler: saveChanges - }, - '->', - { - text: 'Help', - handler: function() { - new tvheadend.help('XMLTV configuration', 'config_xmltv.html'); - } - } - ] - } - ); + var simplePanel = new Ext.form.FieldSet({ + title : 'Simple Config', + height : 120, + width : 900, + items : [ + simpleModule, + intervalValue, + intervalUnit + ] + }); - /* - * Event handlers + /* **************************************************************** + * Advanced Config + * ***************************************************************/ + + /* + * Schedule */ - advancedCheck.on('enable', function(e) { - Ext.MessageBox.alert('Test', 'testing'); - }); - - moduleSelect.on('select', function(c,r,i) { - alert('select module'); - }); - - confpanel.on('render', function() { - confpanel.getForm().load({ - url:'xmltv', - params:{'op':'loadSettings'}, - success:function(form, action) { - confpanel.enable(); - } - }); - }); - - - grabberSelect.on('select', function(c,r,i) { - - Ext.MessageBox.alert('XMLTV', - 'Make sure that the grabber is properly ' + - 'configured before saving configuration.
'+ - '
' + - 'To configure manually execute the ' + - 'following command in a shell on the ' + - 'server:
' + - '$ ' + r.data.identifier + - ' --configure
' + - '
' + - 'Note: It is important to configure the ' + - 'grabber using the same userid as tvheadend '+ - 'since most grabbers save their '+ - 'configuration in the users home directory.'+ - '
' + - '
' + - 'Grabber version: ' + r.data.version - ); - -/* - if(r.data.apiconfig) { - - Ext.MessageBox.confirm('XMLTV', - 'Configure grabber? ' + - 'If you know that the grabber is already '+ - 'set up or if you want to configure it '+ - 'manually you may skip this step', - function(button) { - Ext.MessageBox.alert('XMLTV', - 'oops, embedded '+ - 'config not '+ - 'implemeted yet'); - } - ); - - } else { - Ext.MessageBox.alert('XMLTV', - 'This grabber does not support being ' + - 'configured from external application ' + - '(such as Tvheadend).
' + - 'Make sure that the grabber is properly ' + - 'configured before saving configuration.
'+ - '
' + - 'To configure manually execute the ' + - 'following command in a shell on the ' + - 'server:
' + - '$ ' + r.data.identifier + - ' --configure
' + - '
' + - 'Note: It is important to configure the ' + - 'grabber using the same userid as tvheadend '+ - 'since most grabbers save their '+ - 'configuration in the users home directory.'+ - '
' + - '
' + - 'Grabber version: ' + r.data.version - ); - } -*/ - }); - - function saveChanges() { - confpanel.getForm().submit({ - url:'xmltv', - params:{'op':'saveSettings'}, - waitMsg:'Saving Data...', - failure: function(form, action) { - Ext.Msg.alert('Save failed', action.result.errormsg); - } - }); + var scheduleColumnModel = new Ext.grid.ColumnModel([ + { + header : 'Module', + dataIndex : 'module', + width : 300, + sortable : false, + renderer : function (v) + { + if ( v != "" ) { + i = advancedModuleStore.find('id', v); + v = advancedModuleStore.getAt(i).get('name'); + } + return v; + }, + editor : new Ext.form.ComboBox({ + valueField : 'id', + displayField : 'name', + editable : false, + mode : 'local', + triggerAction : 'all', + store : advancedModuleStore + }), + }, + { + header : 'Cron', + dataIndex : 'cron', + width : 150, + sortable : false, + editor : new Ext.form.TextField() + }, + { + header : 'Command', + dataIndex : 'command', + width : 200, + sortable : false, + editor : new Ext.form.TextField() + }, + { + dataIndex : 'options', + header : 'Options', + width : 200, + sortable : false, + editor : new Ext.form.TextField() } + ]); + scheduleColumnModel.isCellEditable = function (ci, ri) + { + if (ci == 0) return true; + m = scheduleStore.getAt(ri).get('module'); + if (m == "") return false; + m = advancedModuleStore.find('id', m); + m = advancedModuleStore.getAt(m); + if (!(m.get('flags') & EPGGRAB_MODULE_ADVANCED)) { + return (ci == 1); + } + return true; + }; - return confpanel; + scheduleSelectModel = new Ext.grid.RowSelectionModel({ + singleSelect : false, + }); + scheduleSelectModel.on('selectionchange', function(s) { + delButton.setDisabled(s.getCount() == 0); + }); + + var addButton = new Ext.Button({ + text : 'Add', + iconCls : 'add', + handler : function () { + scheduleStore.add(new scheduleRow({ + module : '', + cron : '', + command : '', + options : ''} + )); + } + }); + + var delButton = new Ext.Button({ + text : 'Delete', + iconCls : 'remove', + disabled : true, + handler : function () { + var s = schedulePanel.getSelectionModel().each(function(r){ + scheduleStore.remove(r); + }); + } + }); + + var schedulePanel = new Ext.grid.EditorGridPanel({ + store : scheduleStore, + cm : scheduleColumnModel, + sm : scheduleSelectModel, + width : 850, + height : 150, + frame : true, + viewConfig : { + forceFit : true, + markDirty : false + }, + iconCls : 'icon-grid', + tbar : [ + addButton, + delButton + ], + listeners : { + 'afteredit' : function (r) { + if ( r.field == 'module' ) { + d = scheduleStore.getAt(r.row); + c = ''; + if ( r.value != "" ) { + i = advancedModuleStore.find('id', r.value); + m = advancedModuleStore.getAt(i); + c = m.get('path'); + } + d.set('command', c) + } + }, + 'select' : function(r) { + delButton.setDisabled(false); + } + } + }); + + var advancedPanel = new Ext.form.FieldSet({ + title : 'Advanced Config', + height : 200, + width : 900, + items : [ + // TODO: external editors + schedulePanel + ] + }); + + /* **************************************************************** + * Form + * ***************************************************************/ + + var advancedCheck = new Ext.form.Checkbox({ + fieldLabel : 'Advanced Config', + name : 'advanced', + listeners : { + 'check' : function (e, v) { + simplePanel.setVisible(!v); + advancedPanel.setVisible(v); + } + } + }); + + var eitCheck = new Ext.form.Checkbox({ + fieldLabel : 'EIT Enabled', + name : 'eitenabled' + }); + + var saveButton = new Ext.Button({ + text : "Save configuration", + tooltip : 'Save changes made to configuration below', + iconCls :'save', + handler : saveChanges, + }); + + var helpButton = new Ext.Button({ + text : 'Help', + handler : function() { + alert('TODO: help info'); + } + }); + + var confpanel = new Ext.FormPanel({ + title : 'EPG Grabber', + iconCls : 'xml', + border : false, + bodyStyle : 'padding:15px', + labelAlign : 'left', + labelWidth : 150, + waitMsgTarget : true, + reader : confreader, + layout : 'form', + defaultType : 'textfield', + items : [ + interval, + advancedCheck, + eitCheck, + simplePanel, + advancedPanel + ], + tbar: [ + saveButton, + '->', + helpButton + ] + }); + + // TODO: HACK: bug in extjs seems to cause sub-components of the form not to render! + confpanel.on('afterlayout', function() + { + simplePanel.syncSize(); + advancedPanel.syncSize(); + simplePanel.setVisible(!advancedCheck.getValue()); + advancedPanel.setVisible(advancedCheck.getValue()); + }); + + /* **************************************************************** + * Load/Save + * ***************************************************************/ + + confpanel.on('render', function() { + confpanel.getForm().load({ + url : 'epggrab', + params : { op : 'loadSettings' }, + success : function ( form, action ) { + confpanel.enable(); + } + }); + }); + + function saveChanges() { + data = []; + scheduleStore.each(function(r) { + data.push(r.data); + }); + json = Ext.util.JSON.encode(data); + confpanel.getForm().submit({ + url : 'epggrab', + params : { op : 'saveSettings', schedule : json }, + waitMsg : 'Saving Data...', + success : function(form, action) { + scheduleStore.commitChanges(); + }, + failure : function (form, action) { + Ext.Msg.alert('Save failed', action.result.errormsg); + } + }); + } + + return confpanel; }