From 26705e93108e5b722832173f5a83145937a94670 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 30 Jun 2014 20:30:54 +0200 Subject: [PATCH] epggrab: ota - add multi-line cron scheduler - the initial EPG scan can be turned off in webui - the OTA mux timeout is configurable through webui --- src/epggrab.c | 14 ++- src/epggrab.h | 7 ++ src/epggrab/otamux.c | 169 +++++++++++++++++++++++++++++--- src/webui/extjs.c | 13 ++- src/webui/static/app/epggrab.js | 27 ++++- 5 files changed, 214 insertions(+), 16 deletions(-) diff --git a/src/epggrab.c b/src/epggrab.c index 62c77bc4..7f060712 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -158,10 +158,10 @@ static void _epggrab_load ( void ) gtimer_arm(&epggrab_save_timer, epg_save_callback, NULL, epggrab_epgdb_periodicsave); if ((str = htsmsg_get_str(m, "cron")) == NULL) { + str = buf; if (!htsmsg_get_u32(m, old ? "grab-interval" : "interval", &interval)) { if (old) interval *= 3600; - str = buf; if (interval <= 600) strcpy(buf, "*/10 * * * *"); else if (interval <= 900) @@ -209,6 +209,10 @@ static void _epggrab_load ( void ) } } } + htsmsg_get_u32(m, "ota_timeout", &epggrab_ota_timeout); + htsmsg_get_u32(m, "ota_initial", &epggrab_ota_initial); + if ((str = htsmsg_get_str(m, "ota_cron")) != NULL) + epggrab_ota_set_cron(str, 0); htsmsg_destroy(m); /* Finish up migration */ @@ -247,7 +251,7 @@ static void _epggrab_load ( void ) /* Defaults */ } else { free(epggrab_cron); - epggrab_cron = strdup("4 */12 * * *"); // each 12 hours (noon) + epggrab_cron = strdup("# Default config (00:04 and 12:04 everyday)\n4 */12 * * *"); epggrab_module = NULL; // disabled LIST_FOREACH(mod, &epggrab_modules, link) // enable all OTA by default if (mod->type == EPGGRAB_OTA) @@ -278,6 +282,9 @@ void epggrab_save ( void ) htsmsg_add_u32(m, "channel_renumber", epggrab_channel_renumber); htsmsg_add_u32(m, "epgdb_periodicsave", epggrab_epgdb_periodicsave); htsmsg_add_str(m, "cron", epggrab_cron); + htsmsg_add_str(m, "ota_cron", epggrab_ota_cron); + htsmsg_add_u32(m, "ota_timeout", epggrab_ota_timeout); + htsmsg_add_u32(m, "ota_initial", epggrab_ota_initial); if ( epggrab_module ) htsmsg_add_str(m, "module", epggrab_module->id); a = NULL; @@ -437,6 +444,9 @@ void epggrab_init ( void ) /* Load config */ _epggrab_load(); + /* Post-init for OTA subsystem */ + epggrab_ota_post(); + /* Start internal grab thread */ epggrab_running = 1; tvhthread_create(&epggrab_tid, NULL, _epggrab_internal_thread, NULL, 0); diff --git a/src/epggrab.h b/src/epggrab.h index 7956aea0..b42b83ae 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -255,6 +255,9 @@ extern uint32_t epggrab_channel_rename; extern uint32_t epggrab_channel_renumber; extern uint32_t epggrab_channel_reicon; extern uint32_t epggrab_epgdb_periodicsave; +extern char *epggrab_ota_cron; +extern uint32_t epggrab_ota_timeout; +extern uint32_t epggrab_ota_initial; /* * Set configuration @@ -268,6 +271,9 @@ int epggrab_set_channel_reicon ( uint32_t e ); int epggrab_set_periodicsave ( uint32_t e ); int epggrab_enable_module ( epggrab_module_t *mod, uint8_t e ); int epggrab_enable_module_by_id ( const char *id, uint8_t e ); +int epggrab_ota_set_cron ( const char *cron, int lock ); +int epggrab_ota_set_timeout ( uint32_t e ); +int epggrab_ota_set_initial ( uint32_t e ); /* * Load/Save @@ -276,6 +282,7 @@ void epggrab_init ( void ); void epggrab_done ( void ); void epggrab_save ( void ); void epggrab_ota_init ( void ); +void epggrab_ota_post ( void ); void epggrab_ota_shutdown ( void ); /* ************************************************************************** diff --git a/src/epggrab/otamux.c b/src/epggrab/otamux.c index 47653373..225fb5fc 100644 --- a/src/epggrab/otamux.c +++ b/src/epggrab/otamux.c @@ -24,14 +24,15 @@ #include "epggrab/private.h" #include "input.h" #include "subscriptions.h" +#include "cron.h" #include #include #include #include -#define EPGGRAB_OTA_MIN_INTERVAL 300 #define EPGGRAB_OTA_MIN_TIMEOUT 30 +#define EPGGRAB_OTA_MAX_TIMEOUT 7200 #define EPGGRAB_OTA_DONE_COMPLETE 0 #define EPGGRAB_OTA_DONE_TIMEOUT 1 @@ -39,12 +40,21 @@ typedef TAILQ_HEAD(epggrab_ota_head,epggrab_ota_mux) epggrab_ota_head_t; +uint32_t epggrab_ota_initial; +char *epggrab_ota_cron; +cron_multi_t *epggrab_ota_cron_multi; +uint32_t epggrab_ota_timeout; + RB_HEAD(,epggrab_ota_mux) epggrab_ota_all; epggrab_ota_head_t epggrab_ota_pending; epggrab_ota_head_t epggrab_ota_active; gtimer_t epggrab_ota_kick_timer; -gtimer_t epggrab_ota_pending_timer; +gtimer_t epggrab_ota_start_timer; + +int epggrab_ota_pending_flag; + +pthread_mutex_t epggrab_ota_mutex; SKEL_DECLARE(epggrab_ota_mux_skel, epggrab_ota_mux_t); SKEL_DECLARE(epggrab_svc_link_skel, epggrab_ota_svc_link_t); @@ -73,12 +83,14 @@ om_svcl_cmp ( epggrab_ota_svc_link_t *a, epggrab_ota_svc_link_t *b ) } static int -epggrab_ota_timeout ( void ) +epggrab_ota_timeout_get ( void ) { - int timeout = 600; + int timeout = epggrab_ota_timeout; if (timeout < EPGGRAB_OTA_MIN_TIMEOUT) timeout = EPGGRAB_OTA_MIN_TIMEOUT; + if (timeout > EPGGRAB_OTA_MAX_TIMEOUT) + timeout = EPGGRAB_OTA_MAX_TIMEOUT; return timeout; } @@ -86,6 +98,19 @@ epggrab_ota_timeout ( void ) static void epggrab_ota_kick ( int delay ) { + epggrab_ota_mux_t *om; + + if (TAILQ_EMPTY(&epggrab_ota_pending) && + TAILQ_EMPTY(&epggrab_ota_active)) { + /* next round is pending? queue all ota muxes */ + if (epggrab_ota_pending_flag) { + epggrab_ota_pending_flag = 0; + RB_FOREACH(om, &epggrab_ota_all, om_global_link) + TAILQ_INSERT_TAIL(&epggrab_ota_pending, om, om_q_link); + } else { + return; + } + } gtimer_arm(&epggrab_ota_kick_timer, epggrab_ota_kick_cb, NULL, delay); } @@ -121,7 +146,7 @@ epggrab_ota_start ( epggrab_ota_mux_t *om, int grace ) TAILQ_INSERT_TAIL(&epggrab_ota_active, om, om_q_link); gtimer_arm(&om->om_timer, epggrab_ota_timeout_cb, om, - epggrab_ota_timeout() + grace); + epggrab_ota_timeout_get() + grace); LIST_FOREACH(map, &om->om_modules, om_link) { map->om_first = 1; map->om_complete = 0; @@ -361,6 +386,55 @@ done: goto next_one; } +/* + * Start times management + */ + +static void +epggrab_ota_start_cb ( void *p ) +{ + time_t next; + + tvhtrace("epggrab", "ota start callback"); + + epggrab_ota_pending_flag = 1; + + /* Finish previous job? */ + if (TAILQ_EMPTY(&epggrab_ota_pending) && + TAILQ_EMPTY(&epggrab_ota_active)) { + tvhtrace("epggrab", "ota - idle - kicked"); + epggrab_ota_kick(1); + } + + pthread_mutex_lock(&epggrab_ota_mutex); + if (!cron_multi_next(epggrab_ota_cron_multi, dispatch_clock, &next)) { + tvhtrace("epggrab", "next ota start event in %li seconds", next - time(NULL)); + gtimer_arm_abs(&epggrab_ota_start_timer, epggrab_ota_start_cb, NULL, next); + } + pthread_mutex_unlock(&epggrab_ota_mutex); +} + +static void +epggrab_ota_arm ( time_t last ) +{ + time_t next; + + pthread_mutex_lock(&epggrab_ota_mutex); + + if (!cron_multi_next(epggrab_ota_cron_multi, time(NULL), &next)) { + /* do not trigger the next EPG scan for 1/2 hour */ + if (last != (time_t)-1 && last + 1800 > next) + next = last + 1800; + tvhtrace("epggrab", "next ota start event in %li seconds", next - time(NULL)); + gtimer_arm_abs(&epggrab_ota_start_timer, epggrab_ota_start_cb, NULL, next); + } + pthread_mutex_unlock(&epggrab_ota_mutex); +} + +/* + * Service management + */ + void epggrab_ota_service_add ( epggrab_ota_map_t *map, epggrab_ota_mux_t *ota, const char *uuid, int save ) @@ -447,7 +521,6 @@ epggrab_ota_load_one free(ota); return; } - TAILQ_INSERT_TAIL(&epggrab_ota_pending, ota, om_q_link); if (!(l = htsmsg_get_list(c, "modules"))) return; HTSMSG_FOREACH(f, l) { @@ -475,6 +548,18 @@ epggrab_ota_init ( void ) char path[1024]; struct stat st; + epggrab_ota_initial = 1; + epggrab_ota_timeout = 600; + epggrab_ota_cron = strdup("# Default config (02:04 and 14:04 everyday)\n4 2 * * *\n4 14 * * *");; + epggrab_ota_cron_multi = NULL; + epggrab_ota_pending_flag = 0; + + RB_INIT(&epggrab_ota_all); + TAILQ_INIT(&epggrab_ota_pending); + TAILQ_INIT(&epggrab_ota_active); + + pthread_mutex_init(&epggrab_ota_mutex, NULL); + /* Add listener */ static mpegts_listener_t ml = { .ml_mux_start = epggrab_mux_start, @@ -482,9 +567,6 @@ epggrab_ota_init ( void ) }; mpegts_add_listener(&ml); - TAILQ_INIT(&epggrab_ota_pending); - TAILQ_INIT(&epggrab_ota_active); - /* Delete old config */ hts_settings_buildpath(path, sizeof(path), "epggrab/otamux"); if (!lstat(path, &st)) @@ -499,10 +581,22 @@ epggrab_ota_init ( void ) } htsmsg_destroy(c); } - +} + +void +epggrab_ota_post ( void ) +{ + time_t t = (time_t)-1; + /* Init timer (call after full init - wait for network tuners) */ - if (TAILQ_FIRST(&epggrab_ota_pending)) + if (epggrab_ota_initial) { + epggrab_ota_pending_flag = 1; epggrab_ota_kick(15); + t = time(NULL); + } + + /* arm the first scheduled time */ + epggrab_ota_arm(t); } static void @@ -535,6 +629,59 @@ epggrab_ota_shutdown ( void ) pthread_mutex_unlock(&global_lock); SKEL_FREE(epggrab_ota_mux_skel); SKEL_FREE(epggrab_svc_link_skel); + free(epggrab_ota_cron); + epggrab_ota_cron = NULL; + free(epggrab_ota_cron_multi); + epggrab_ota_cron_multi = NULL; +} + +/* + * Global configuration handlers + */ + +int +epggrab_ota_set_cron ( const char *cron, int lock ) +{ + int save = 0; + if ( epggrab_ota_cron == NULL || strcmp(epggrab_ota_cron, cron) ) { + save = 1; + pthread_mutex_lock(&epggrab_ota_mutex); + free(epggrab_ota_cron); + epggrab_ota_cron = strdup(cron); + free(epggrab_ota_cron_multi); + epggrab_ota_cron_multi = cron_multi_set(cron); + pthread_mutex_unlock(&epggrab_ota_mutex); + if (lock) { + pthread_mutex_lock(&global_lock); + epggrab_ota_arm((time_t)-1); + pthread_mutex_unlock(&global_lock); + } else { + epggrab_ota_arm((time_t)-1); + } + } + return save; +} + +int +epggrab_ota_set_timeout( uint32_t e ) +{ + int save = 0; + if (epggrab_ota_timeout != e) { + epggrab_ota_timeout = e; + save = 1; + } + return save; +} + +int +epggrab_ota_set_initial( uint32_t e ) +{ + int save = 0; + if (epggrab_ota_initial != e) { + epggrab_ota_initial = e; + save = 1; + } + return save; } /****************************************************************************** diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 4055b6b9..b76a43ef 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -432,11 +432,14 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) r = htsmsg_create_map(); if (epggrab_module) htsmsg_add_str(r, "module", epggrab_module->id); - htsmsg_add_str(r, "cron", epggrab_cron); + htsmsg_add_str(r, "cron", epggrab_cron ? epggrab_cron : ""); htsmsg_add_u32(r, "channel_rename", epggrab_channel_rename); htsmsg_add_u32(r, "channel_renumber", epggrab_channel_renumber); htsmsg_add_u32(r, "channel_reicon", epggrab_channel_reicon); htsmsg_add_u32(r, "epgdb_periodicsave", epggrab_epgdb_periodicsave / 3600); + htsmsg_add_str(r, "ota_cron", epggrab_ota_cron ? epggrab_ota_cron : ""); + htsmsg_add_u32(r, "ota_timeout", epggrab_ota_timeout); + htsmsg_add_u32(r, "ota_initial", epggrab_ota_initial); pthread_mutex_unlock(&epggrab_mutex); out = json_single_record(r, "epggrabSettings"); @@ -463,6 +466,14 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) save |= epggrab_set_periodicsave(atoi(str) * 3600); if ( (str = http_arg_get(&hc->hc_req_args, "cron")) ) save |= epggrab_set_cron(str); + if ( (str = http_arg_get(&hc->hc_req_args, "ota_cron")) ) + save |= epggrab_ota_set_cron(str, 1); + if ( (str = http_arg_get(&hc->hc_req_args, "ota_timeout")) ) + save |= epggrab_ota_set_timeout(atoi(str)); + str = http_arg_get(&hc->hc_req_args, "ota_initial"); + save |= epggrab_ota_set_initial(str ? 1 : 0); + if ( (str = http_arg_get(&hc->hc_req_args, "epgdb_periodicsave")) ) + save |= epggrab_set_periodicsave(atoi(str) * 3600); 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, "external")) ) { diff --git a/src/webui/static/app/epggrab.js b/src/webui/static/app/epggrab.js index a6e232f7..be21184a 100644 --- a/src/webui/static/app/epggrab.js +++ b/src/webui/static/app/epggrab.js @@ -83,7 +83,8 @@ tvheadend.epggrab = function() { var confreader = new Ext.data.JsonReader({ root: 'epggrabSettings' }, ['module', 'cron', 'channel_rename', 'channel_renumber', - 'channel_reicon', 'epgdb_periodicsave']); + 'channel_reicon', 'epgdb_periodicsave', + 'ota_cron', 'ota_timeout', 'ota_initial']); /* **************************************************************** * Basic Fields @@ -255,13 +256,35 @@ tvheadend.epggrab = function() { iconCls: 'icon-grid' }); + var otaInitial = new Ext.form.Checkbox({ + name: 'ota_initial', + fieldLabel: 'Force initial EPG scan at startup' + }); + + var otaCron = new Ext.form.TextArea({ + fieldLabel: 'Over-the-air Cron multi-line', + name: 'ota_cron', + width: 300, + }); + + var otaTimeout = new Ext.form.NumberField({ + width: 30, + allowNegative: false, + allowDecimals: false, + minValue: 30, + maxValue: 7200, + value: 600, + fieldLabel: 'EPG scan timeout in seconds (30-7200)', + name: 'ota_timeout' + }); + var otaPanel = new Ext.form.FieldSet({ title: 'Over-the-air Grabbers', width: 700, autoHeight: true, collapsible: true, collapsed: true, - items: [otaGrid] + items: [otaInitial, otaCron, otaTimeout, otaGrid] }); /* ****************************************************************