tvheadend/src/epggrab.c
2012-12-19 22:30:16 +00:00

378 lines
9.5 KiB
C

/*
* Electronic Program Guide - Common functions
* Copyright (C) 2012 Adam Sutton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <wordexp.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/socket.h>
#include "htsmsg.h"
#include "settings.h"
#include "tvheadend.h"
#include "queue.h"
#include "epg.h"
#include "epggrab.h"
#include "epggrab/private.h"
#include "channels.h"
#include "spawn.h"
#include "htsmsg_xml.h"
#include "file.h"
#include "service.h"
/* Thread protection */
static int epggrab_confver;
pthread_mutex_t epggrab_mutex;
static pthread_cond_t epggrab_cond;
/* Config */
uint32_t epggrab_interval;
epggrab_module_int_t* epggrab_module;
epggrab_module_list_t epggrab_modules;
uint32_t epggrab_channel_rename;
uint32_t epggrab_channel_renumber;
uint32_t epggrab_channel_reicon;
/* **************************************************************************
* Internal Grab Thread
* *************************************************************************/
/*
* Grab from module
*/
static void _epggrab_module_grab ( epggrab_module_int_t *mod )
{
time_t tm1, tm2;
htsmsg_t *data;
/* Grab */
time(&tm1);
data = mod->trans(mod, mod->grab(mod));
time(&tm2);
/* Process */
if ( data ) {
tvhlog(LOG_INFO, mod->id, "grab took %"PRItime_t" seconds", tm2 - tm1);
epggrab_module_parse(mod, data);
} else {
tvhlog(LOG_WARNING, mod->id, "grab returned no data");
}
}
/*
* Thread (for internal grabbing)
*/
static void* _epggrab_internal_thread ( void* p )
{
int err, confver = -1; // force first run
struct timespec ts;
epggrab_module_int_t *mod;
/* Setup timeout */
ts.tv_nsec = 0;
time(&ts.tv_sec);
while ( 1 ) {
/* Check for config change */
pthread_mutex_lock(&epggrab_mutex);
while ( confver == epggrab_confver ) {
if (epggrab_module) {
err = pthread_cond_timedwait(&epggrab_cond, &epggrab_mutex, &ts);
} else {
err = pthread_cond_wait(&epggrab_cond, &epggrab_mutex);
}
if ( err == ETIMEDOUT ) break;
}
confver = epggrab_confver;
mod = epggrab_module;
ts.tv_sec += epggrab_interval;
pthread_mutex_unlock(&epggrab_mutex);
/* Run grabber */
if (mod) _epggrab_module_grab(mod);
}
return NULL;
}
/* **************************************************************************
* Configuration
* *************************************************************************/
static void _epggrab_load ( void )
{
epggrab_module_t *mod;
htsmsg_field_t *f;
htsmsg_t *m, *a;
uint32_t enabled = 1;
const char *str;
int old = 0;
/* Load settings */
if (!(m = hts_settings_load("epggrab/config"))) {
if ((m = hts_settings_load("xmltv/config")))
old = 1;
}
if (old) tvhlog(LOG_INFO, "epggrab", "migrating old configuration");
/* Process */
if (m) {
htsmsg_get_u32(m, "channel_rename", &epggrab_channel_rename);
htsmsg_get_u32(m, "channel_renumber", &epggrab_channel_renumber);
htsmsg_get_u32(m, "channel_reicon", &epggrab_channel_reicon);
if (!htsmsg_get_u32(m, old ? "grab-interval" : "interval",
&epggrab_interval)) {
if (old) epggrab_interval *= 3600;
}
htsmsg_get_u32(m, "grab-enabled", &enabled);
if (enabled) {
if ( (str = htsmsg_get_str(m, old ? "current-grabber" : "module")) ) {
mod = epggrab_module_find_by_id(str);
if (mod && mod->type == EPGGRAB_INT) {
epggrab_module = (epggrab_module_int_t*)mod;
}
}
if ( (a = htsmsg_get_map(m, "mod_enabled")) ) {
LIST_FOREACH(mod, &epggrab_modules, link) {
if (htsmsg_get_u32_or_default(a, mod->id, 0)) {
epggrab_enable_module(mod, 1);
}
}
}
if ( (a = htsmsg_get_list(m, "mod_priority")) ) {
int prio = 1;
LIST_FOREACH(mod, &epggrab_modules, link)
mod->priority = 0;
HTSMSG_FOREACH(f, a) {
mod = epggrab_module_find_by_id(f->hmf_str);
if (mod) mod->priority = prio++;
}
}
}
htsmsg_destroy(m);
/* Finish up migration */
if (old) {
/* Enable OTA modules */
LIST_FOREACH(mod, &epggrab_modules, link)
if (mod->type == EPGGRAB_OTA)
epggrab_enable_module(mod, 1);
/* Migrate XMLTV channels */
htsmsg_t *xc, *ch;
htsmsg_t *xchs = hts_settings_load("xmltv/channels");
htsmsg_t *chs = hts_settings_load("channels");
if (xchs) {
HTSMSG_FOREACH(f, chs) {
if ((ch = htsmsg_get_map_by_field(f))) {
if ((str = htsmsg_get_str(ch, "xmltv-channel"))) {
if ((xc = htsmsg_get_map(xchs, str))) {
htsmsg_add_u32(xc, "channel", atoi(f->hmf_name));
}
}
}
}
HTSMSG_FOREACH(f, xchs) {
if ((xc = htsmsg_get_map_by_field(f))) {
hts_settings_save(xc, "epggrab/xmltv/channels/%s", f->hmf_name);
}
}
}
/* Save epggrab config */
epggrab_save();
}
/* Defaults */
} else {
epggrab_interval = 12 * 3600; // hours
epggrab_module = NULL; // disabled
LIST_FOREACH(mod, &epggrab_modules, link) // enable all OTA by default
if (mod->type == EPGGRAB_OTA)
epggrab_enable_module(mod, 1);
}
/* Load module config (channels) */
#if ENABLE_LINUXDVB
eit_load();
opentv_load();
#endif
pyepg_load();
xmltv_load();
}
void epggrab_save ( void )
{
epggrab_module_t *mod;
htsmsg_t *m, *a;
/* Register */
epggrab_confver++;
pthread_cond_signal(&epggrab_cond);
/* Save */
m = htsmsg_create_map();
htsmsg_add_u32(m, "channel_rename", epggrab_channel_rename);
htsmsg_add_u32(m, "channel_renumber", epggrab_channel_renumber);
htsmsg_add_u32(m, "channel_reicon", epggrab_channel_reicon);
htsmsg_add_u32(m, "interval", epggrab_interval);
if ( epggrab_module )
htsmsg_add_str(m, "module", epggrab_module->id);
a = NULL;
LIST_FOREACH(mod, &epggrab_modules, link) {
if (mod->enabled) {
if (!a) a = htsmsg_create_map();
htsmsg_add_u32(a, mod->id, 1);
}
}
if (a) htsmsg_add_msg(m, "mod_enabled", a);
hts_settings_save(m, "epggrab/config");
htsmsg_destroy(m);
}
int epggrab_set_interval ( uint32_t interval )
{
int save = 0;
if ( epggrab_interval != interval ) {
save = 1;
epggrab_interval = interval;
}
return save;
}
int epggrab_set_module ( epggrab_module_t *m )
{
int save = 0;
epggrab_module_int_t *mod;
if (m && m->type != EPGGRAB_INT) return 0;
mod = (epggrab_module_int_t*)m;
if ( epggrab_module != mod ) {
epggrab_enable_module((epggrab_module_t*)epggrab_module, 0);
epggrab_module = (epggrab_module_int_t*)mod;
epggrab_enable_module((epggrab_module_t*)epggrab_module, 1);
save = 1;
}
return save;
}
int epggrab_set_module_by_id ( const char *id )
{
return epggrab_set_module(epggrab_module_find_by_id(id));
}
int epggrab_set_channel_rename ( uint32_t e )
{
int save = 0;
if ( e != epggrab_channel_rename ) {
epggrab_channel_rename = e;
save = 1;
}
return save;
}
int epggrab_set_channel_renumber ( uint32_t e )
{
int save = 0;
if ( e != epggrab_channel_renumber ) {
epggrab_channel_renumber = e;
save = 1;
}
return save;
}
int epggrab_set_channel_reicon ( uint32_t e )
{
int save = 0;
if ( e != epggrab_channel_reicon ) {
epggrab_channel_reicon = e;
save = 1;
}
return save;
}
int epggrab_enable_module ( epggrab_module_t *mod, uint8_t e )
{
int save = 0;
if (!mod) return 0;
if (mod->enable) {
save = mod->enable(mod, e);
} else if ( e != mod->enabled ) {
mod->enabled = e;
save = 1;
}
return save;
}
int epggrab_enable_module_by_id ( const char *id, uint8_t e )
{
return epggrab_enable_module(epggrab_module_find_by_id(id), e);
}
/* **************************************************************************
* Initialisation
* *************************************************************************/
/*
* TODO: implement this
*/
void epggrab_resched ( void )
{
}
/*
* Initialise
*/
void epggrab_init ( void )
{
/* Lists */
#if ENABLE_LINUXDVB
extern TAILQ_HEAD(, epggrab_ota_mux) ota_mux_all;
TAILQ_INIT(&ota_mux_all);
#endif
pthread_mutex_init(&epggrab_mutex, NULL);
pthread_cond_init(&epggrab_cond, NULL);
/* Initialise modules */
#if ENABLE_LINUXDVB
eit_init();
opentv_init();
#endif
pyepg_init();
xmltv_init();
/* Load config */
_epggrab_load();
#if ENABLE_LINUXDVB
epggrab_ota_load();
#endif
/* Start internal grab thread */
pthread_t tid;
pthread_attr_t tattr;
pthread_attr_init(&tattr);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &tattr, _epggrab_internal_thread, NULL);
pthread_attr_destroy(&tattr);
}