Merge branch 'epg-rewrite' of github.com:adamsutton/tvheadend into epg-rewrite
This commit is contained in:
commit
a72fcf60d8
22 changed files with 1946 additions and 1511 deletions
12
Makefile
12
Makefile
|
@ -75,11 +75,13 @@ SRCS = src/main.c \
|
|||
src/avc.c \
|
||||
src/huffman.c \
|
||||
|
||||
SRCS += src/epggrab/pyepg.c\
|
||||
src/epggrab/xmltv.c\
|
||||
src/epggrab/ota.c \
|
||||
src/epggrab/eit.c \
|
||||
src/epggrab/opentv.c \
|
||||
SRCS += src/epggrab/module.c\
|
||||
src/epggrab/channel.c\
|
||||
src/epggrab/otamux.c\
|
||||
src/epggrab/module/pyepg.c\
|
||||
src/epggrab/module/xmltv.c\
|
||||
src/epggrab/module/eit.c \
|
||||
src/epggrab/module/opentv.c \
|
||||
|
||||
SRCS += src/plumbing/tsfix.c \
|
||||
src/plumbing/globalheaders.c \
|
||||
|
|
|
@ -115,8 +115,6 @@ typedef struct th_dvb_mux_instance {
|
|||
|
||||
int tdmi_enabled;
|
||||
|
||||
LIST_HEAD(, epggrab_ota_mux) tdmi_epg_grabbers;
|
||||
|
||||
time_t tdmi_got_adapter;
|
||||
time_t tdmi_lost_adapter;
|
||||
|
||||
|
@ -132,10 +130,10 @@ typedef struct th_dvb_mux_instance {
|
|||
|
||||
struct service_list tdmi_transports; /* via s_mux_link */
|
||||
|
||||
|
||||
TAILQ_ENTRY(th_dvb_mux_instance) tdmi_scan_link;
|
||||
struct th_dvb_mux_instance_queue *tdmi_scan_queue;
|
||||
|
||||
TAILQ_HEAD(, epggrab_ota_mux) tdmi_epg_grab;
|
||||
|
||||
} th_dvb_mux_instance_t;
|
||||
|
||||
|
@ -147,8 +145,7 @@ typedef struct th_dvb_mux_instance {
|
|||
|
||||
#define TDA_SCANQ_BAD 0 ///< Bad muxes (monitor quality)
|
||||
#define TDA_SCANQ_OK 1 ///< OK muxes
|
||||
#define TDA_SCANQ_EPG 2 ///< EPG muxes (TBD)
|
||||
#define TDA_SCANQ_NUM 3
|
||||
#define TDA_SCANQ_NUM 2
|
||||
|
||||
typedef struct th_dvb_adapter {
|
||||
|
||||
|
@ -164,6 +161,8 @@ typedef struct th_dvb_adapter {
|
|||
|
||||
th_dvb_mux_instance_t *tda_mux_current;
|
||||
|
||||
th_dvb_mux_instance_t *tda_mux_epg;
|
||||
|
||||
int tda_table_epollfd;
|
||||
|
||||
const char *tda_rootpath;
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
#include "tsdemux.h"
|
||||
#include "notify.h"
|
||||
#include "service.h"
|
||||
#include "epggrab/ota.h"
|
||||
#include "epggrab.h"
|
||||
|
||||
struct th_dvb_adapter_queue dvb_adapters;
|
||||
struct th_dvb_mux_instance_tree dvb_muxes;
|
||||
|
@ -420,16 +420,10 @@ dvb_adapter_init(uint32_t adapter_mask)
|
|||
void
|
||||
dvb_adapter_mux_scanner(void *aux)
|
||||
{
|
||||
epggrab_ota_mux_t *ota;
|
||||
th_dvb_adapter_t *tda = aux;
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
int i;
|
||||
int idle_epg;
|
||||
static const char* scan_string[] = {
|
||||
"Autoscan BAD",
|
||||
"Autoscan OK",
|
||||
"Autoscan EPG"
|
||||
};
|
||||
|
||||
if(tda->tda_rootpath == NULL)
|
||||
return; // No hardware
|
||||
|
@ -449,15 +443,16 @@ dvb_adapter_mux_scanner(void *aux)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Mark any incomplete EPG grabbers as timed out (basically complete) */
|
||||
if (tda->tda_mux_current)
|
||||
LIST_FOREACH(ota, &tda->tda_mux_current->tdmi_epg_grabbers, tdmi_link) {
|
||||
epggrab_ota_timeout(ota);
|
||||
}
|
||||
/* Check EPG */
|
||||
if (tda->tda_mux_epg) {
|
||||
epggrab_mux_stop(tda->tda_mux_epg, 1); // timeout anything not complete
|
||||
tda->tda_mux_epg = NULL; // skip this time
|
||||
} else {
|
||||
tda->tda_mux_epg = epggrab_mux_next(tda);
|
||||
}
|
||||
|
||||
/* Idle or EPG scan enabled */
|
||||
idle_epg = tda->tda_idlescan ||
|
||||
TAILQ_FIRST(&tda->tda_scan_queues[TDA_SCANQ_EPG]);
|
||||
idle_epg = tda->tda_idlescan || tda->tda_mux_epg;
|
||||
|
||||
/* Idlescan is disabled and no muxes are bad */
|
||||
if(!idle_epg && TAILQ_FIRST(&tda->tda_scan_queues[TDA_SCANQ_BAD]) == NULL) {
|
||||
|
@ -472,27 +467,27 @@ dvb_adapter_mux_scanner(void *aux)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Alternate between the three queues (BAD, OK, EPG) */
|
||||
for(i = 0; i < TDA_SCANQ_NUM; i++) {
|
||||
tda->tda_scan_selector++;
|
||||
if (tda->tda_scan_selector == TDA_SCANQ_NUM)
|
||||
tda->tda_scan_selector = 0;
|
||||
/* EPG */
|
||||
if (tda->tda_mux_epg) {
|
||||
int period = epggrab_mux_period(tda->tda_mux_epg);
|
||||
if (period > 20)
|
||||
gtimer_arm(&tda->tda_mux_scanner_timer,
|
||||
dvb_adapter_mux_scanner, tda, period);
|
||||
dvb_fe_tune(tda->tda_mux_epg, "EPG scan");
|
||||
|
||||
tdmi = TAILQ_FIRST(&tda->tda_scan_queues[tda->tda_scan_selector]);
|
||||
if(tdmi != NULL) {
|
||||
/* Normal */
|
||||
} else {
|
||||
|
||||
/* EPG - adjust dwell time */
|
||||
if (tda->tda_scan_selector == TDA_SCANQ_EPG) {
|
||||
epggrab_ota_mux_t *ota;
|
||||
int period = 20;
|
||||
LIST_FOREACH(ota, &tdmi->tdmi_epg_grabbers, tdmi_link) {
|
||||
if (ota->timeout > period) period = ota->timeout;
|
||||
}
|
||||
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, period);
|
||||
/* Alternate queue */
|
||||
for(i = 0; i < TDA_SCANQ_NUM; i++) {
|
||||
tda->tda_scan_selector++;
|
||||
if (tda->tda_scan_selector == TDA_SCANQ_NUM)
|
||||
tda->tda_scan_selector = 0;
|
||||
tdmi = TAILQ_FIRST(&tda->tda_scan_queues[tda->tda_scan_selector]);
|
||||
if (tdmi) {
|
||||
dvb_fe_tune(tdmi, "Autoscan");
|
||||
return;
|
||||
}
|
||||
|
||||
dvb_fe_tune(tdmi, scan_string[tda->tda_scan_selector]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -230,6 +230,8 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi)
|
|||
if(tdmi->tdmi_enabled) {
|
||||
dvb_mux_add_to_scan_queue(tdmi);
|
||||
}
|
||||
|
||||
epggrab_mux_stop(tdmi, 0);
|
||||
|
||||
time(&tdmi->tdmi_lost_adapter);
|
||||
}
|
||||
|
@ -509,7 +511,7 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
|
|||
|
||||
|
||||
dvb_table_add_default(tdmi);
|
||||
epggrab_tune(tdmi);
|
||||
epggrab_mux_start(tdmi);
|
||||
|
||||
dvb_adapter_notify(tda);
|
||||
return 0;
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
#include "dvb_support.h"
|
||||
#include "notify.h"
|
||||
#include "subscriptions.h"
|
||||
#include "epggrab/ota.h"
|
||||
#include "epggrab.h"
|
||||
|
||||
struct th_dvb_mux_instance_tree dvb_muxes;
|
||||
|
||||
|
@ -289,6 +289,8 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc,
|
|||
mux_link_initial(tda, tdmi);
|
||||
}
|
||||
|
||||
TAILQ_INIT(&tdmi->tdmi_epg_grab);
|
||||
|
||||
return tdmi;
|
||||
}
|
||||
|
||||
|
@ -298,7 +300,6 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc,
|
|||
void
|
||||
dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
|
||||
{
|
||||
epggrab_ota_mux_t *ota;
|
||||
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
|
||||
service_t *t;
|
||||
|
||||
|
@ -332,8 +333,7 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
|
|||
if(tdmi->tdmi_table_initial)
|
||||
tda->tda_initial_num_mux--;
|
||||
|
||||
while ((ota = LIST_FIRST(&tdmi->tdmi_epg_grabbers)))
|
||||
epggrab_ota_unregister(ota);
|
||||
epggrab_mux_delete(tdmi);
|
||||
|
||||
hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier);
|
||||
|
||||
|
@ -1157,9 +1157,8 @@ void dvb_mux_add_to_scan_queue ( th_dvb_mux_instance_t *tdmi )
|
|||
{
|
||||
int ti;
|
||||
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
|
||||
ti = LIST_FIRST(&tdmi->tdmi_epg_grabbers) ? TDA_SCANQ_EPG
|
||||
: tdmi->tdmi_quality == 100 ? TDA_SCANQ_OK
|
||||
: TDA_SCANQ_BAD;
|
||||
ti = tdmi->tdmi_quality == 100 ? TDA_SCANQ_OK
|
||||
: TDA_SCANQ_BAD;
|
||||
tdmi->tdmi_scan_queue = &tda->tda_scan_queues[ti];
|
||||
TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
|
||||
}
|
||||
|
|
673
src/epggrab.c
673
src/epggrab.c
|
@ -13,10 +13,7 @@
|
|||
#include "queue.h"
|
||||
#include "epg.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/eit.h"
|
||||
#include "epggrab/xmltv.h"
|
||||
#include "epggrab/pyepg.h"
|
||||
#include "epggrab/opentv.h"
|
||||
#include "epggrab/private.h"
|
||||
#include "channels.h"
|
||||
#include "spawn.h"
|
||||
#include "htsmsg_xml.h"
|
||||
|
@ -30,59 +27,20 @@ pthread_cond_t epggrab_cond;
|
|||
|
||||
/* Config */
|
||||
uint32_t epggrab_interval;
|
||||
epggrab_module_t* epggrab_module;
|
||||
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;
|
||||
|
||||
/* **************************************************************************
|
||||
* Helpers
|
||||
* Internal Grab Thread
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* Run the parse
|
||||
*/
|
||||
static void _epggrab_module_parse
|
||||
( epggrab_module_t *mod, htsmsg_t *data )
|
||||
{
|
||||
time_t tm1, tm2;
|
||||
int save = 0;
|
||||
epggrab_stats_t stats;
|
||||
|
||||
/* Parse */
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
pthread_mutex_lock(&global_lock);
|
||||
time(&tm1);
|
||||
save |= mod->parse(mod, data, &stats);
|
||||
time(&tm2);
|
||||
if (save) epg_updated();
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
htsmsg_destroy(data);
|
||||
|
||||
/* Debug stats */
|
||||
tvhlog(LOG_INFO, mod->id, "parse took %d seconds", tm2 - tm1);
|
||||
tvhlog(LOG_INFO, mod->id, " channels tot=%5d new=%5d mod=%5d",
|
||||
stats.channels.total, stats.channels.created,
|
||||
stats.channels.modified);
|
||||
tvhlog(LOG_INFO, mod->id, " brands tot=%5d new=%5d mod=%5d",
|
||||
stats.brands.total, stats.brands.created,
|
||||
stats.brands.modified);
|
||||
tvhlog(LOG_INFO, mod->id, " seasons tot=%5d new=%5d mod=%5d",
|
||||
stats.seasons.total, stats.seasons.created,
|
||||
stats.seasons.modified);
|
||||
tvhlog(LOG_INFO, mod->id, " episodes tot=%5d new=%5d mod=%5d",
|
||||
stats.episodes.total, stats.episodes.created,
|
||||
stats.episodes.modified);
|
||||
tvhlog(LOG_INFO, mod->id, " broadcasts tot=%5d new=%5d mod=%5d",
|
||||
stats.broadcasts.total, stats.broadcasts.created,
|
||||
stats.broadcasts.modified);
|
||||
}
|
||||
|
||||
/*
|
||||
* Grab from module
|
||||
*/
|
||||
static void _epggrab_module_grab ( epggrab_module_t *mod )
|
||||
static void _epggrab_module_grab ( epggrab_module_int_t *mod )
|
||||
{
|
||||
time_t tm1, tm2;
|
||||
htsmsg_t *data;
|
||||
|
@ -95,43 +53,12 @@ static void _epggrab_module_grab ( epggrab_module_t *mod )
|
|||
/* Process */
|
||||
if ( data ) {
|
||||
tvhlog(LOG_INFO, mod->id, "grab took %d seconds", tm2 - tm1);
|
||||
_epggrab_module_parse(mod, data);
|
||||
epggrab_module_parse(mod, data);
|
||||
} else {
|
||||
tvhlog(LOG_WARNING, mod->id, "grab returned no data");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Socket handler
|
||||
*/
|
||||
static void _epggrab_socket_handler ( epggrab_module_t *mod, int s )
|
||||
{
|
||||
size_t outlen;
|
||||
char *outbuf;
|
||||
time_t tm1, tm2;
|
||||
htsmsg_t *data = NULL;
|
||||
|
||||
/* Grab/Translate */
|
||||
time(&tm1);
|
||||
outlen = file_readall(s, &outbuf);
|
||||
if (outlen) data = mod->trans(mod, outbuf);
|
||||
time(&tm2);
|
||||
|
||||
/* Process */
|
||||
if ( data ) {
|
||||
tvhlog(LOG_INFO, mod->id, "grab took %d seconds", tm2 - tm1);
|
||||
_epggrab_module_parse(mod, data);
|
||||
|
||||
/* Failed */
|
||||
} else {
|
||||
tvhlog(LOG_ERR, mod->id, "failed to read data");
|
||||
}
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Threads
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* Thread (for internal grabbing)
|
||||
*/
|
||||
|
@ -139,7 +66,7 @@ static void* _epggrab_internal_thread ( void* p )
|
|||
{
|
||||
int err, confver = -1; // force first run
|
||||
struct timespec ts;
|
||||
epggrab_module_t *mod;
|
||||
epggrab_module_int_t *mod;
|
||||
|
||||
/* Setup timeout */
|
||||
ts.tv_nsec = 0;
|
||||
|
@ -169,476 +96,6 @@ static void* _epggrab_internal_thread ( void* p )
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* External (socket) grab thread
|
||||
*/
|
||||
static void *_epggrab_socket_thread ( void *p )
|
||||
{
|
||||
int s;
|
||||
epggrab_module_t *mod = (epggrab_module_t*)p;
|
||||
tvhlog(LOG_INFO, mod->id, "external socket enabled");
|
||||
|
||||
while ( mod->enabled && mod->sock ) {
|
||||
tvhlog(LOG_DEBUG, mod->id, "waiting for connection");
|
||||
s = accept(mod->sock, NULL, NULL);
|
||||
if (s <= 0) continue;
|
||||
tvhlog(LOG_DEBUG, mod->id, "got connection %d", s);
|
||||
_epggrab_socket_handler(mod, s);
|
||||
}
|
||||
tvhlog(LOG_DEBUG, mod->id, "terminated");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Base Module functions
|
||||
* *************************************************************************/
|
||||
|
||||
static int _ch_id_cmp ( void *a, void *b )
|
||||
{
|
||||
return strcmp(((epggrab_channel_t*)a)->id,
|
||||
((epggrab_channel_t*)b)->id);
|
||||
}
|
||||
|
||||
static int _ch_link ( epggrab_channel_t *ec, channel_t *ch )
|
||||
{
|
||||
service_t *sv;
|
||||
int match = 0, i;
|
||||
|
||||
if (!ec || !ch) return 0;
|
||||
if (ec->channel) return 0;
|
||||
|
||||
if (ec->name && !strcmp(ec->name, ch->ch_name))
|
||||
match = 1;
|
||||
else {
|
||||
LIST_FOREACH(sv, &ch->ch_services, s_ch_link) {
|
||||
if (ec->sid) {
|
||||
for (i = 0; i < ec->sid_cnt; i++ ) {
|
||||
if (sv->s_dvb_service_id == ec->sid[i]) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!match && ec->sname) {
|
||||
i = 0;
|
||||
while (ec->sname[i]) {
|
||||
if (!strcmp(ec->sname[i], sv->s_svcname)) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (match) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
tvhlog(LOG_INFO, ec->mod->id, "linking %s to %s",
|
||||
ec->id, ch->ch_name);
|
||||
ec->channel = ch;
|
||||
if (ec->name && epggrab_channel_rename)
|
||||
channel_rename(ch, ec->name);
|
||||
if (ec->icon && epggrab_channel_reicon)
|
||||
channel_set_icon(ch, ec->icon);
|
||||
if (ec->number>0 && epggrab_channel_renumber)
|
||||
channel_set_number(ch, ec->number);
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
int epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e )
|
||||
{
|
||||
pthread_t tid;
|
||||
pthread_attr_t tattr;
|
||||
struct sockaddr_un addr;
|
||||
assert(mod->path);
|
||||
assert(mod->flags & EPGGRAB_MODULE_EXTERNAL);
|
||||
|
||||
/* Ignore */
|
||||
if ( mod->enabled == e ) return 0;
|
||||
|
||||
/* Disable */
|
||||
if (!e) {
|
||||
shutdown(mod->sock, SHUT_RDWR);
|
||||
close(mod->sock);
|
||||
unlink(mod->path);
|
||||
mod->sock = 0;
|
||||
|
||||
/* Enable */
|
||||
} else {
|
||||
unlink(mod->path); // just in case!
|
||||
|
||||
mod->sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
assert(mod->sock);
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, mod->path, 100);
|
||||
if ( bind(mod->sock, (struct sockaddr*)&addr,
|
||||
sizeof(struct sockaddr_un)) != 0 ) {
|
||||
tvhlog(LOG_ERR, mod->id, "failed to bind socket");
|
||||
close(mod->sock);
|
||||
mod->sock = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( listen(mod->sock, 5) != 0 ) {
|
||||
tvhlog(LOG_ERR, mod->id, "failed to listen on socket");
|
||||
close(mod->sock);
|
||||
mod->sock = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tvhlog(LOG_DEBUG, mod->id, "starting socket thread");
|
||||
pthread_attr_init(&tattr);
|
||||
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&tid, &tattr, _epggrab_socket_thread, mod);
|
||||
}
|
||||
mod->enabled = e;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *epggrab_module_socket_path ( const char *id )
|
||||
{
|
||||
char *ret = malloc(100);
|
||||
snprintf(ret, 100, "%s/epggrab/%s.sock",
|
||||
hts_settings_get_root(), id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *epggrab_module_grab ( epggrab_module_t *mod )
|
||||
{
|
||||
int outlen;
|
||||
char *outbuf;
|
||||
|
||||
/* Debug */
|
||||
tvhlog(LOG_INFO, mod->id, "grab %s", mod->path);
|
||||
|
||||
/* Grab */
|
||||
outlen = spawn_and_store_stdout(mod->path, NULL, &outbuf);
|
||||
if ( outlen < 1 ) {
|
||||
tvhlog(LOG_ERR, "pyepg", "no output detected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return outbuf;
|
||||
}
|
||||
|
||||
htsmsg_t *epggrab_module_trans_xml
|
||||
( epggrab_module_t *mod, char *c )
|
||||
{
|
||||
htsmsg_t *ret;
|
||||
char errbuf[100];
|
||||
|
||||
if (!c) return NULL;
|
||||
|
||||
/* Extract */
|
||||
ret = htsmsg_xml_deserialize(c, errbuf, sizeof(errbuf));
|
||||
if (!ret)
|
||||
tvhlog(LOG_ERR, "pyepg", "htsmsg_xml_deserialize error %s", errbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void epggrab_module_channel_save
|
||||
( epggrab_module_t *mod, epggrab_channel_t *ch )
|
||||
{
|
||||
int i;
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_t *a;
|
||||
|
||||
if (ch->name)
|
||||
htsmsg_add_str(m, "name", ch->name);
|
||||
if (ch->icon)
|
||||
htsmsg_add_str(m, "icon", ch->icon);
|
||||
if (ch->channel)
|
||||
htsmsg_add_u32(m, "channel", ch->channel->ch_id);
|
||||
if (ch->sid) {
|
||||
a = htsmsg_create_list();
|
||||
for (i = 0; i < ch->sid_cnt; i++ ) {
|
||||
htsmsg_add_u32(a, NULL, ch->sid[i]);
|
||||
}
|
||||
htsmsg_add_msg(m, "sid", a);
|
||||
}
|
||||
if (ch->sname) {
|
||||
a = htsmsg_create_list();
|
||||
i = 0;
|
||||
while (ch->sname[i]) {
|
||||
htsmsg_add_str(a, NULL, ch->sname[i]);
|
||||
i++;
|
||||
}
|
||||
htsmsg_add_msg(m, "sname", a);
|
||||
}
|
||||
if (ch->number)
|
||||
htsmsg_add_u32(m, "number", ch->number);
|
||||
|
||||
hts_settings_save(m, "epggrab/%s/channels/%s", mod->id, ch->id);
|
||||
}
|
||||
|
||||
static void epggrab_module_channel_load
|
||||
( epggrab_module_t *mod, htsmsg_t *m, const char *id )
|
||||
{
|
||||
int save = 0, i;
|
||||
const char *str;
|
||||
uint32_t u32;
|
||||
htsmsg_t *a;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
epggrab_channel_t *ch = epggrab_module_channel_find(mod, id, 1, &save);
|
||||
|
||||
if ((str = htsmsg_get_str(m, "name")))
|
||||
ch->name = strdup(str);
|
||||
if ((str = htsmsg_get_str(m, "icon")))
|
||||
ch->icon = strdup(str);
|
||||
if ((a = htsmsg_get_list(m, "sid"))) {
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) i++;
|
||||
ch->sid_cnt = i;
|
||||
ch->sid = calloc(i, sizeof(uint16_t));
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) {
|
||||
ch->sid[i] = (uint16_t)f->hmf_s64;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if ((a = htsmsg_get_list(m, "sname"))) {
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) i++;
|
||||
ch->sname = calloc(i+1, sizeof(char*));
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) {
|
||||
ch->sname[i] = strdup(f->hmf_str);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if(!htsmsg_get_u32(m, "number", &u32))
|
||||
ch->number = u32;
|
||||
|
||||
if (!htsmsg_get_u32(m, "channel", &u32))
|
||||
ch->channel = channel_find_by_identifier(u32);
|
||||
}
|
||||
|
||||
void epggrab_module_channels_load ( epggrab_module_t *mod )
|
||||
{
|
||||
htsmsg_t *m, *e;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
if ((m = hts_settings_load("epggrab/%s/channels", mod->id))) {
|
||||
HTSMSG_FOREACH(f, m) {
|
||||
if ((e = htsmsg_get_map_by_field(f)))
|
||||
epggrab_module_channel_load(mod, e, f->hmf_name);
|
||||
}
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_module_channel_add ( epggrab_module_t *mod, channel_t *ch )
|
||||
{
|
||||
epggrab_channel_t *egc;
|
||||
RB_FOREACH(egc, mod->channels, link) {
|
||||
if (_ch_link(egc, ch) ) {
|
||||
epggrab_module_channel_save(mod, egc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_module_channel_rem ( epggrab_module_t *mod, channel_t *ch )
|
||||
{
|
||||
epggrab_channel_t *egc;
|
||||
RB_FOREACH(egc, mod->channels, link) {
|
||||
if (egc->channel == ch) {
|
||||
egc->channel = NULL;
|
||||
epggrab_module_channel_save(mod, egc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_module_channel_mod ( epggrab_module_t *mod, channel_t *ch )
|
||||
{
|
||||
return epggrab_module_channel_add(mod, ch);
|
||||
}
|
||||
|
||||
|
||||
epggrab_channel_t *epggrab_module_channel_find
|
||||
( epggrab_module_t *mod, const char *id, int create, int *save )
|
||||
{
|
||||
epggrab_channel_t *ec;
|
||||
static epggrab_channel_t *skel = NULL;
|
||||
|
||||
if (!mod || !mod->channels ) return NULL;
|
||||
|
||||
if ( !skel ) skel = calloc(1, sizeof(epggrab_channel_t));
|
||||
skel->id = (char*)id;
|
||||
skel->mod = mod;
|
||||
|
||||
/* Find */
|
||||
if (!create) {
|
||||
ec = RB_FIND(mod->channels, skel, link, _ch_id_cmp);
|
||||
|
||||
/* Create (if required) */
|
||||
} else {
|
||||
ec = RB_INSERT_SORTED(mod->channels, skel, link, _ch_id_cmp);
|
||||
if ( ec == NULL ) {
|
||||
skel->id = strdup(skel->id);
|
||||
ec = skel;
|
||||
skel = NULL;
|
||||
*save = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Channels
|
||||
* *************************************************************************/
|
||||
|
||||
int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
|
||||
{
|
||||
int save = 0;
|
||||
if (!ec || !name) return 0;
|
||||
if (!ec->name || strcmp(ec->name, name)) {
|
||||
if (ec->name) free(ec->name);
|
||||
ec->name = strdup(name);
|
||||
if (ec->channel && epggrab_channel_rename)
|
||||
channel_rename(ec->channel, name);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
int epggrab_channel_set_sid
|
||||
( epggrab_channel_t *ec, const uint16_t *sid, int num )
|
||||
{
|
||||
int save = 0, i = 0;
|
||||
if ( !ec || !sid ) return 0;
|
||||
if (!ec->sid) save = 1;
|
||||
else if (ec->sid_cnt != num) save = 1;
|
||||
else {
|
||||
for (i = 0; i < num; i++ ) {
|
||||
if (sid[i] != ec->sid[i]) {
|
||||
save = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (save) {
|
||||
if (ec->sid) free(ec->sid);
|
||||
ec->sid = calloc(num, sizeof(uint16_t));
|
||||
for (i = 0; i < num; i++ ) {
|
||||
ec->sid[i] = sid[i];
|
||||
}
|
||||
ec->sid_cnt = num;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
int epggrab_channel_set_sname ( epggrab_channel_t *ec, const char **sname )
|
||||
{
|
||||
int save = 0, i = 0;
|
||||
if ( !ec || !sname ) return 0;
|
||||
if (!ec->sname) save = 1;
|
||||
else {
|
||||
while ( ec->sname[i] && sname[i] ) {
|
||||
if (strcmp(ec->sname[i], sname[i])) {
|
||||
save = 1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!save && (ec->sname[i] || sname[i])) save = 1;
|
||||
}
|
||||
if (save) {
|
||||
if (ec->sname) {
|
||||
i = 0;
|
||||
while (ec->sname[i])
|
||||
free(ec->sname[i++]);
|
||||
free(ec->sname);
|
||||
}
|
||||
i = 0;
|
||||
while (sname[i++]);
|
||||
ec->sname = calloc(i+1, sizeof(char*));
|
||||
i = 0;
|
||||
while (sname[i]) {
|
||||
ec->sname[i] = strdup(sname[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
|
||||
{
|
||||
int save = 0;
|
||||
if (!ec->icon || strcmp(ec->icon, icon) ) {
|
||||
if (!ec | !icon) return 0;
|
||||
if (ec->icon) free(ec->icon);
|
||||
ec->icon = strdup(icon);
|
||||
if (ec->channel && epggrab_channel_reicon)
|
||||
channel_set_icon(ec->channel, icon);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
|
||||
{
|
||||
int save = 0;
|
||||
if (!ec || (number <= 0)) return 0;
|
||||
if (ec->number != number) {
|
||||
ec->number = number;
|
||||
if (ec->channel && epggrab_channel_renumber)
|
||||
channel_set_number(ec->channel, number);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
void epggrab_channel_link ( epggrab_channel_t *ec )
|
||||
{
|
||||
channel_t *ch;
|
||||
|
||||
if (!ec) return;
|
||||
|
||||
/* Link */
|
||||
if (!ec->channel) {
|
||||
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
|
||||
if (_ch_link(ec, ch)) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_channel_updated ( epggrab_channel_t *ec )
|
||||
{
|
||||
epggrab_channel_link(ec);
|
||||
epggrab_module_channel_save(ec->mod, ec);
|
||||
}
|
||||
|
||||
htsmsg_t *epggrab_channel_list ( void )
|
||||
{
|
||||
char name[100];
|
||||
epggrab_module_t *mod;
|
||||
epggrab_channel_t *ec;
|
||||
htsmsg_t *e, *m;
|
||||
m = htsmsg_create_list();
|
||||
LIST_FOREACH(mod, &epggrab_modules, link) {
|
||||
if (mod->enabled && mod->channels) {
|
||||
RB_FOREACH(ec, mod->channels, link) {
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_str(e, "module", mod->id);
|
||||
htsmsg_add_str(e, "id", ec->id);
|
||||
sprintf(name, "%s: %s", mod->name, ec->name);
|
||||
htsmsg_add_str(e, "name", name);
|
||||
htsmsg_add_msg(m, NULL, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Configuration
|
||||
* *************************************************************************/
|
||||
|
@ -667,12 +124,16 @@ static void _epggrab_load ( void )
|
|||
if (old) epggrab_interval *= 3600;
|
||||
htsmsg_get_u32(m, "grab-enabled", &enabled);
|
||||
if (enabled) {
|
||||
if ( (str = htsmsg_get_str(m, old ? "current-grabber" : "module")) )
|
||||
epggrab_module = epggrab_module_find_by_id(str);
|
||||
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)) {
|
||||
if (mod->enable) mod->enable(mod, 1);
|
||||
epggrab_enable_module(mod, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -712,7 +173,7 @@ static void _epggrab_load ( void )
|
|||
epggrab_interval = 12 * 3600; // hours
|
||||
epggrab_module = NULL; // disabled
|
||||
LIST_FOREACH(m, &epggrab_modules, link) // enable all OTA by default
|
||||
if (m->flags & EPGGRAB_MODULE_OTA)
|
||||
if (m->type == EPGGRAB_OTA)
|
||||
epggrab_enable_module(m, 1);
|
||||
}
|
||||
|
||||
|
@ -762,18 +223,16 @@ int epggrab_set_interval ( uint32_t interval )
|
|||
return save;
|
||||
}
|
||||
|
||||
int epggrab_set_module ( epggrab_module_t *mod )
|
||||
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 ) {
|
||||
if (epggrab_module) epggrab_enable_module(epggrab_module, 0);
|
||||
if (mod) {
|
||||
assert(mod->grab);
|
||||
assert(mod->trans);
|
||||
assert(mod->parse);
|
||||
}
|
||||
epggrab_module = mod;
|
||||
if (epggrab_module) epggrab_enable_module(epggrab_module, 1);
|
||||
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;
|
||||
|
@ -817,6 +276,7 @@ int epggrab_set_channel_reicon ( uint32_t e )
|
|||
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 ) {
|
||||
|
@ -832,19 +292,30 @@ int epggrab_enable_module_by_id ( const char *id, uint8_t e )
|
|||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Global Functions
|
||||
* Initialisation
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* TODO: implement this
|
||||
*/
|
||||
void epggrab_resched ( void )
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise
|
||||
*/
|
||||
void epggrab_init ( void )
|
||||
{
|
||||
/* Lists */
|
||||
extern TAILQ_HEAD(, epggrab_ota_mux) ota_mux_all;
|
||||
TAILQ_INIT(&ota_mux_all);
|
||||
|
||||
/* Initialise modules */
|
||||
eit_init(&epggrab_modules);
|
||||
xmltv_init(&epggrab_modules);
|
||||
pyepg_init(&epggrab_modules);
|
||||
opentv_init(&epggrab_modules);
|
||||
eit_init();
|
||||
xmltv_init();
|
||||
pyepg_init();
|
||||
opentv_init();
|
||||
|
||||
/* Load config */
|
||||
_epggrab_load();
|
||||
|
@ -857,71 +328,3 @@ void epggrab_init ( void )
|
|||
pthread_create(&tid, &tattr, _epggrab_internal_thread, NULL);
|
||||
}
|
||||
|
||||
void epggrab_channel_add ( channel_t *ch )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if (m->ch_add) m->ch_add(m, ch);
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_channel_rem ( channel_t *ch )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if (m->ch_rem) m->ch_rem(m, ch);
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_channel_mod ( channel_t *ch )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if (m->ch_mod) m->ch_mod(m, ch);
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_tune ( th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
epggrab_ota_mux_t *o;
|
||||
th_dvb_mux_instance_t *t;
|
||||
|
||||
/* Cancel all currently active ota grabs */
|
||||
LIST_FOREACH(t, &tdmi->tdmi_adapter->tda_muxes, tdmi_adapter_link) {
|
||||
if (t == tdmi) continue;
|
||||
LIST_FOREACH(o, &t->tdmi_epg_grabbers, tdmi_link) {
|
||||
epggrab_ota_cancel(o);
|
||||
}
|
||||
}
|
||||
|
||||
/* Inform all modules */
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if (m->tune) m->tune(m, tdmi);
|
||||
}
|
||||
}
|
||||
|
||||
epggrab_module_t* epggrab_module_find_by_id ( const char *id )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if ( !strcmp(m->id, id) ) return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
htsmsg_t *epggrab_module_list ( void )
|
||||
{
|
||||
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);
|
||||
if(m->path) htsmsg_add_str(e, "path", m->path);
|
||||
htsmsg_add_u32(e, "flags", m->flags);
|
||||
htsmsg_add_u32(e, "enabled", m->enabled);
|
||||
htsmsg_add_msg(a, NULL, e);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
|
215
src/epggrab.h
215
src/epggrab.h
|
@ -1,12 +1,41 @@
|
|||
/*
|
||||
* EPG Grabber - 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __EPGGRAB_H__
|
||||
#define __EPGGRAB_H__
|
||||
|
||||
typedef struct epggrab_module epggrab_module_t;
|
||||
|
||||
#include <pthread.h>
|
||||
#include "channels.h"
|
||||
#include "dvb/dvb.h"
|
||||
#include "epggrab/ota.h"
|
||||
|
||||
/* **************************************************************************
|
||||
* Typedefs/Forward decls
|
||||
* *************************************************************************/
|
||||
|
||||
struct th_dvb_mux_instance;
|
||||
struct th_dvb_adapter;
|
||||
|
||||
typedef struct epggrab_module epggrab_module_t;
|
||||
typedef struct epggrab_module_int epggrab_module_int_t;
|
||||
typedef struct epggrab_module_ext epggrab_module_ext_t;
|
||||
typedef struct epggrab_module_ota epggrab_module_ota_t;
|
||||
typedef struct epggrab_ota_mux epggrab_ota_mux_t;
|
||||
|
||||
LIST_HEAD(epggrab_module_list, epggrab_module);
|
||||
typedef struct epggrab_module_list epggrab_module_list_t;
|
||||
|
||||
/* **************************************************************************
|
||||
* Grabber Stats
|
||||
|
@ -41,14 +70,14 @@ typedef struct epggrab_channel
|
|||
epggrab_module_t *mod; ///< Linked module
|
||||
|
||||
char *id; ///< Grabber's ID
|
||||
|
||||
char *name; ///< Channel name
|
||||
char **sname; ///< Service name's
|
||||
uint16_t *sid; ///< Service ID's
|
||||
int sid_cnt; ///< Number of service IDs
|
||||
|
||||
char *icon; ///< Channel icon
|
||||
int number; ///< Channel number
|
||||
|
||||
char **sname; ///< Service name's
|
||||
uint16_t *sid; ///< Service ID's
|
||||
|
||||
struct channel *channel; ///< Mapped channel
|
||||
} epggrab_channel_t;
|
||||
|
||||
|
@ -67,88 +96,113 @@ htsmsg_t* epggrab_channel_list ( void );
|
|||
* Mutators
|
||||
*/
|
||||
int epggrab_channel_set_name ( epggrab_channel_t *ch, const char *name );
|
||||
int epggrab_channel_set_sid ( epggrab_channel_t *ch, const uint16_t *sid, int num );
|
||||
int epggrab_channel_set_sname ( epggrab_channel_t *ch, const char **sname );
|
||||
int epggrab_channel_set_icon ( epggrab_channel_t *ch, const char *icon );
|
||||
int epggrab_channel_set_number ( epggrab_channel_t *ch, int number );
|
||||
|
||||
void epggrab_channel_updated ( epggrab_channel_t *ch );
|
||||
void epggrab_channel_link ( epggrab_channel_t *ch );
|
||||
int epggrab_channel_set_sname ( epggrab_channel_t *ch, const char **sname );
|
||||
int epggrab_channel_set_sid ( epggrab_channel_t *ch, const uint16_t *sid );
|
||||
|
||||
/*
|
||||
* Match the channel
|
||||
* Updated/link
|
||||
*/
|
||||
void epggrab_channel_updated ( epggrab_channel_t *ch );
|
||||
|
||||
/* **************************************************************************
|
||||
* Grabber Modules
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* Grabber flags
|
||||
*/
|
||||
#define EPGGRAB_MODULE_INTERNAL 0x01 ///< IP based internally run
|
||||
#define EPGGRAB_MODULE_EXTERNAL 0x02 ///< IP based externally run
|
||||
#define EPGGRAB_MODULE_OTA 0x04 ///< DVB OTA EPGs
|
||||
|
||||
/*
|
||||
* Grabber base class
|
||||
*/
|
||||
struct epggrab_module
|
||||
{
|
||||
LIST_ENTRY(epggrab_module) link; ///< Global list link
|
||||
LIST_ENTRY(epggrab_module) link; ///< Global list link
|
||||
|
||||
enum {
|
||||
EPGGRAB_INT,
|
||||
EPGGRAB_EXT,
|
||||
EPGGRAB_OTA
|
||||
} type; ///< Grabber type
|
||||
const char *id; ///< Module identifier
|
||||
const char *name; ///< Module name (for display)
|
||||
const char *path; ///< Module path (for fixed config)
|
||||
const uint8_t flags; ///< Mode flag
|
||||
uint8_t enabled; ///< Whether the module is enabled
|
||||
int sock; ///< Socket descriptor
|
||||
epggrab_channel_tree_t *channels; ///< Channel list
|
||||
LIST_HEAD(, epggrab_ota_mux) muxes; ///< Linked muxes
|
||||
|
||||
/* Enable/Disable */
|
||||
int (*enable) ( epggrab_module_t *m, uint8_t e );
|
||||
|
||||
/* Grab/Translate/Parse */
|
||||
char* (*grab) ( epggrab_module_t *m );
|
||||
htsmsg_t* (*trans) ( epggrab_module_t *m, char *d );
|
||||
int (*parse) ( epggrab_module_t *m,
|
||||
htsmsg_t *d, epggrab_stats_t *s );
|
||||
int (*enable) ( void *m, uint8_t e );
|
||||
|
||||
/* Channel listings */
|
||||
void (*ch_add) ( epggrab_module_t *m, channel_t *ch );
|
||||
void (*ch_rem) ( epggrab_module_t *m, channel_t *ch );
|
||||
void (*ch_mod) ( epggrab_module_t *m, channel_t *ch );
|
||||
|
||||
/* Transponder tuning */
|
||||
void (*tune) ( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi );
|
||||
void (*ch_add) ( void *m, struct channel *ch );
|
||||
void (*ch_rem) ( void *m, struct channel *ch );
|
||||
void (*ch_mod) ( void *m, struct channel *ch );
|
||||
void (*ch_save) ( void *m, epggrab_channel_t *ch );
|
||||
};
|
||||
|
||||
/*
|
||||
* Default module functions
|
||||
* Internal grabber
|
||||
*/
|
||||
struct epggrab_module_int
|
||||
{
|
||||
epggrab_module_t ; ///< Parent object
|
||||
|
||||
int epggrab_module_enable_socket ( epggrab_module_t *m, uint8_t e );
|
||||
const char *epggrab_module_socket_path ( const char *id );
|
||||
const char *path; ///< Path for the command
|
||||
|
||||
char *epggrab_module_grab ( epggrab_module_t *m );
|
||||
htsmsg_t *epggrab_module_trans_xml ( epggrab_module_t *m, char *d );
|
||||
|
||||
void epggrab_module_channel_save
|
||||
( epggrab_module_t *m, epggrab_channel_t *ch );
|
||||
void epggrab_module_channels_load ( epggrab_module_t *m );
|
||||
void epggrab_module_channel_add ( epggrab_module_t *m, channel_t *ch );
|
||||
void epggrab_module_channel_rem ( epggrab_module_t *m, channel_t *ch );
|
||||
void epggrab_module_channel_mod ( epggrab_module_t *m, channel_t *ch );
|
||||
|
||||
epggrab_channel_t *epggrab_module_channel_find
|
||||
( epggrab_module_t *mod, const char *id, int create, int *save );
|
||||
/* Handle data */
|
||||
char* (*grab) ( void *mod );
|
||||
htsmsg_t* (*trans) ( void *mod, char *data );
|
||||
int (*parse) ( void *mod, htsmsg_t *data, epggrab_stats_t *stat );
|
||||
};
|
||||
|
||||
/*
|
||||
* Module list
|
||||
* External grabber
|
||||
*/
|
||||
LIST_HEAD(epggrab_module_list, epggrab_module);
|
||||
typedef struct epggrab_module_list epggrab_module_list_t;
|
||||
struct epggrab_module_ext
|
||||
{
|
||||
epggrab_module_int_t ; ///< Parent object
|
||||
|
||||
int sock; ///< Socket descriptor
|
||||
};
|
||||
|
||||
/*
|
||||
* OTA / mux link
|
||||
*/
|
||||
struct epggrab_ota_mux
|
||||
{
|
||||
TAILQ_ENTRY(epggrab_ota_mux) glob_link; ///< Grabber link
|
||||
TAILQ_ENTRY(epggrab_ota_mux) tdmi_link; ///< Link to mux
|
||||
TAILQ_ENTRY(epggrab_ota_mux) grab_link; ///< Link to grabber
|
||||
struct th_dvb_mux_instance *tdmi; ///< Mux instance
|
||||
epggrab_module_ota_t *grab; ///< Grab instance
|
||||
|
||||
int timeout; ///< Time out if this long
|
||||
int interval; ///< Re-grab this often
|
||||
|
||||
int is_reg; ///< Permanently registered
|
||||
|
||||
void *status; ///< Status information
|
||||
enum {
|
||||
EPGGRAB_OTA_MUX_IDLE,
|
||||
EPGGRAB_OTA_MUX_RUNNING,
|
||||
EPGGRAB_OTA_MUX_TIMEDOUT,
|
||||
EPGGRAB_OTA_MUX_COMPLETE
|
||||
} state; ///< Current state
|
||||
time_t started; ///< Time of last start
|
||||
time_t completed; ///< Time of last completion
|
||||
|
||||
void (*destroy) (epggrab_ota_mux_t *ota); ///< (Custom) destroy
|
||||
};
|
||||
|
||||
/*
|
||||
* Over the air grabber
|
||||
*/
|
||||
struct epggrab_module_ota
|
||||
{
|
||||
epggrab_module_t ; ///< Parent object
|
||||
|
||||
TAILQ_HEAD(, epggrab_ota_mux) muxes; ///< List of related muxes
|
||||
|
||||
/* Transponder tuning */
|
||||
void (*start) ( epggrab_module_ota_t *m, struct th_dvb_mux_instance *tdmi );
|
||||
};
|
||||
|
||||
/*
|
||||
* Access the Module list
|
||||
|
@ -157,38 +211,42 @@ epggrab_module_t* epggrab_module_find_by_id ( const char *id );
|
|||
htsmsg_t* epggrab_module_list ( void );
|
||||
|
||||
/* **************************************************************************
|
||||
* Configuration
|
||||
* Setup/Configuration
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
extern pthread_mutex_t epggrab_mutex;
|
||||
extern uint32_t epggrab_interval;
|
||||
extern epggrab_module_t* epggrab_module;
|
||||
extern uint32_t epggrab_channel_rename;
|
||||
extern uint32_t epggrab_channel_renumber;
|
||||
extern uint32_t epggrab_channel_reicon;
|
||||
extern epggrab_module_list_t epggrab_modules;
|
||||
extern pthread_mutex_t epggrab_mutex;
|
||||
extern uint32_t epggrab_interval;
|
||||
extern epggrab_module_int_t* epggrab_module;
|
||||
extern uint32_t epggrab_channel_rename;
|
||||
extern uint32_t epggrab_channel_renumber;
|
||||
extern uint32_t epggrab_channel_reicon;
|
||||
|
||||
/*
|
||||
* Update
|
||||
* Set configuration
|
||||
*/
|
||||
int epggrab_set_interval ( uint32_t interval );
|
||||
int epggrab_set_module ( epggrab_module_t *module );
|
||||
int epggrab_set_module ( epggrab_module_t *mod );
|
||||
int epggrab_set_module_by_id ( const char *id );
|
||||
int epggrab_set_channel_rename ( uint32_t e );
|
||||
int epggrab_set_channel_renumber ( uint32_t e );
|
||||
int epggrab_set_channel_reicon ( uint32_t e );
|
||||
int epggrab_enable_module ( epggrab_module_t *module, uint8_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 );
|
||||
|
||||
/*
|
||||
* Load/Save
|
||||
*/
|
||||
void epggrab_init ( void );
|
||||
void epggrab_save ( void );
|
||||
|
||||
/* **************************************************************************
|
||||
* Global Functions
|
||||
* *************************************************************************/
|
||||
|
||||
void epggrab_init ( void );
|
||||
|
||||
/*
|
||||
* Channel handling
|
||||
*/
|
||||
|
@ -199,6 +257,21 @@ void epggrab_channel_mod ( struct channel *ch );
|
|||
/*
|
||||
* Transport handling
|
||||
*/
|
||||
void epggrab_tune ( th_dvb_mux_instance_t *tdmi );
|
||||
void epggrab_mux_start ( struct th_dvb_mux_instance *tdmi );
|
||||
void epggrab_mux_stop ( struct th_dvb_mux_instance *tdmi, int timeout );
|
||||
void epggrab_mux_delete ( struct th_dvb_mux_instance *tdmi );
|
||||
int epggrab_mux_period ( struct th_dvb_mux_instance *tdmi );
|
||||
struct th_dvb_mux_instance *epggrab_mux_next ( struct th_dvb_adapter *tda );
|
||||
|
||||
/*
|
||||
* Re-schedule
|
||||
*/
|
||||
void epggrab_resched ( void );
|
||||
|
||||
#endif /* __EPGGRAB_H__ */
|
||||
|
||||
/* **************************************************************************
|
||||
* Editor
|
||||
*
|
||||
* vim:sts=2:ts=2:sw=2:et
|
||||
* *************************************************************************/
|
||||
|
|
283
src/epggrab/channel.c
Normal file
283
src/epggrab/channel.c
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* EPG Grabber - channel 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 <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tvheadend.h"
|
||||
#include "settings.h"
|
||||
#include "htsmsg.h"
|
||||
#include "channels.h"
|
||||
#include "service.h"
|
||||
#include "epg.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/private.h"
|
||||
|
||||
/* **************************************************************************
|
||||
* EPG Grab Channel functions
|
||||
* *************************************************************************/
|
||||
|
||||
/* Link epggrab channel to real channel */
|
||||
// returns 1 if link made
|
||||
int epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch )
|
||||
{
|
||||
service_t *sv;
|
||||
int match = 0, i;
|
||||
|
||||
if (!ec || !ch) return 0;
|
||||
if (ec->channel) return 0;
|
||||
|
||||
if (ec->name && !strcmp(ec->name, ch->ch_name))
|
||||
match = 1;
|
||||
else {
|
||||
LIST_FOREACH(sv, &ch->ch_services, s_ch_link) {
|
||||
if (ec->sid) {
|
||||
i = 0;
|
||||
while (ec->sid[i]) {
|
||||
if (sv->s_dvb_service_id == ec->sid[i]) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (!match && ec->sname) {
|
||||
i = 0;
|
||||
while (ec->sname[i]) {
|
||||
if (!strcmp(ec->sname[i], sv->s_svcname)) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (match) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
tvhlog(LOG_INFO, ec->mod->id, "linking %s to %s",
|
||||
ec->id, ch->ch_name);
|
||||
ec->channel = ch;
|
||||
if (ec->name && epggrab_channel_rename)
|
||||
channel_rename(ch, ec->name);
|
||||
if (ec->icon && epggrab_channel_reicon)
|
||||
channel_set_icon(ch, ec->icon);
|
||||
if (ec->number>0 && epggrab_channel_renumber)
|
||||
channel_set_number(ch, ec->number);
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/* Set name */
|
||||
int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
|
||||
{
|
||||
int save = 0;
|
||||
if (!ec || !name) return 0;
|
||||
if (!ec->name || strcmp(ec->name, name)) {
|
||||
if (ec->name) free(ec->name);
|
||||
ec->name = strdup(name);
|
||||
if (ec->channel && epggrab_channel_rename)
|
||||
channel_rename(ec->channel, name);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
/* Set icon */
|
||||
int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
|
||||
{
|
||||
int save = 0;
|
||||
if (!ec->icon || strcmp(ec->icon, icon) ) {
|
||||
if (!ec | !icon) return 0;
|
||||
if (ec->icon) free(ec->icon);
|
||||
ec->icon = strdup(icon);
|
||||
if (ec->channel) channel_set_icon(ec->channel, icon);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
/* Set channel number */
|
||||
int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
|
||||
{
|
||||
int save = 0;
|
||||
if (!ec || (number <= 0)) return 0;
|
||||
if (ec->number != number) {
|
||||
ec->number = number;
|
||||
if (ec->channel) channel_set_number(ec->channel, number);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
/* Set service IDs */
|
||||
int epggrab_channel_set_sid
|
||||
( epggrab_channel_t *ec, const uint16_t *sid )
|
||||
{
|
||||
int save = 0, i;
|
||||
if ( !ec || !sid ) return 0;
|
||||
if (!ec->sid) save = 1;
|
||||
else {
|
||||
i = 0;
|
||||
while ( ec->sid[i] && sid[i] ) {
|
||||
if ( ec->sid[i] != sid[i] ) break;
|
||||
i++;
|
||||
}
|
||||
if (ec->sid[i] || sid[i]) save = 1;
|
||||
}
|
||||
if (save) {
|
||||
i = 0;
|
||||
while (ec->sid[i++]);
|
||||
if (ec->sid) free(ec->sid);
|
||||
ec->sid = calloc(i, sizeof(uint16_t));
|
||||
memcpy(ec->sid, sid, i * sizeof(uint16_t));
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
/* Set names */
|
||||
int epggrab_channel_set_sname ( epggrab_channel_t *ec, const char **sname )
|
||||
{
|
||||
int save = 0, i = 0;
|
||||
if ( !ec || !sname ) return 0;
|
||||
if (!ec->sname) save = 1;
|
||||
else {
|
||||
while ( ec->sname[i] && sname[i] ) {
|
||||
if (strcmp(ec->sname[i], sname[i])) break;
|
||||
i++;
|
||||
}
|
||||
if (ec->sname[i] || sname[i]) save = 1;
|
||||
}
|
||||
if (save) {
|
||||
if (ec->sname) {
|
||||
i = 0;
|
||||
while (ec->sname[i])
|
||||
free(ec->sname[i++]);
|
||||
free(ec->sname);
|
||||
}
|
||||
i = 0;
|
||||
while (sname[i++]);
|
||||
ec->sname = calloc(i+1, sizeof(char*));
|
||||
i = 0;
|
||||
while (sname[i]) {
|
||||
ec->sname[i] = strdup(sname[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
/* Channel settings updated */
|
||||
void epggrab_channel_updated ( epggrab_channel_t *ec )
|
||||
{
|
||||
channel_t *ch;
|
||||
if (!ec) return;
|
||||
|
||||
/* Find a link */
|
||||
if (!ec->channel)
|
||||
RB_FOREACH(ch, &channel_name_tree, ch_name_link)
|
||||
if (epggrab_channel_link(ec, ch)) break;
|
||||
|
||||
/* Save */
|
||||
if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec);
|
||||
}
|
||||
|
||||
/* ID comparison */
|
||||
static int _ch_id_cmp ( void *a, void *b )
|
||||
{
|
||||
return strcmp(((epggrab_channel_t*)a)->id,
|
||||
((epggrab_channel_t*)b)->id);
|
||||
}
|
||||
|
||||
/* Find/Create channel in the list */
|
||||
epggrab_channel_t *epggrab_channel_find
|
||||
( epggrab_channel_tree_t *tree, const char *id, int create, int *save )
|
||||
{
|
||||
epggrab_channel_t *ec;
|
||||
static epggrab_channel_t *skel = NULL;
|
||||
if (!skel) skel = calloc(1, sizeof(epggrab_channel_t));
|
||||
skel->id = (char*)id;
|
||||
|
||||
/* Find */
|
||||
if (!create) {
|
||||
ec = RB_FIND(tree, skel, link, _ch_id_cmp);
|
||||
|
||||
/* Find/Create */
|
||||
} else {
|
||||
ec = RB_INSERT_SORTED(tree, skel, link, _ch_id_cmp);
|
||||
if (!ec) {
|
||||
ec = skel;
|
||||
skel = NULL;
|
||||
ec->id = strdup(id);
|
||||
*save = 1;
|
||||
}
|
||||
}
|
||||
return ec;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Global routines
|
||||
* *************************************************************************/
|
||||
|
||||
htsmsg_t *epggrab_channel_list ( void )
|
||||
{
|
||||
char name[100];
|
||||
epggrab_module_t *mod;
|
||||
epggrab_channel_t *ec;
|
||||
htsmsg_t *e, *m;
|
||||
m = htsmsg_create_list();
|
||||
LIST_FOREACH(mod, &epggrab_modules, link) {
|
||||
if (mod->enabled && mod->channels) {
|
||||
RB_FOREACH(ec, mod->channels, link) {
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_str(e, "module", mod->id);
|
||||
htsmsg_add_str(e, "id", ec->id);
|
||||
sprintf(name, "%s: %s", mod->name, ec->name);
|
||||
htsmsg_add_str(e, "name", name);
|
||||
htsmsg_add_msg(m, NULL, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void epggrab_channel_add ( channel_t *ch )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if (m->ch_add) m->ch_add(m, ch);
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_channel_rem ( channel_t *ch )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if (m->ch_rem) m->ch_rem(m, ch);
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_channel_mod ( channel_t *ch )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if (m->ch_mod) m->ch_mod(m, ch);
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Electronic Program Guide - eit grabber
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef EPGGRAB_EIT_H
|
||||
#define EPGGRAB_EIT_H
|
||||
|
||||
#include "epggrab.h"
|
||||
|
||||
void eit_init ( epggrab_module_list_t *list );
|
||||
void eit_load ( void );
|
||||
|
||||
#endif
|
494
src/epggrab/module.c
Normal file
494
src/epggrab/module.c
Normal file
|
@ -0,0 +1,494 @@
|
|||
/*
|
||||
* EPG Grabber - module 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 <assert.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "tvheadend.h"
|
||||
#include "settings.h"
|
||||
#include "htsmsg.h"
|
||||
#include "htsmsg_xml.h"
|
||||
#include "service.h"
|
||||
#include "channels.h"
|
||||
#include "spawn.h"
|
||||
#include "file.h"
|
||||
#include "epg.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/private.h"
|
||||
|
||||
/* **************************************************************************
|
||||
* Module Access
|
||||
* *************************************************************************/
|
||||
|
||||
epggrab_module_t* epggrab_module_find_by_id ( const char *id )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if ( !strcmp(m->id, id) ) return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
htsmsg_t *epggrab_module_list ( void )
|
||||
{
|
||||
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);
|
||||
htsmsg_add_u32(e, "type", m->type);
|
||||
htsmsg_add_u32(e, "enabled", m->enabled);
|
||||
if(m->name)
|
||||
htsmsg_add_str(e, "name", m->name);
|
||||
if(m->type == EPGGRAB_EXT) {
|
||||
epggrab_module_ext_t *ext = (epggrab_module_ext_t*)m;
|
||||
htsmsg_add_str(e, "path", ext->path);
|
||||
}
|
||||
htsmsg_add_msg(a, NULL, e);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Generic module routines
|
||||
* *************************************************************************/
|
||||
|
||||
epggrab_module_t *epggrab_module_create
|
||||
( epggrab_module_t *skel, const char *id, const char *name,
|
||||
epggrab_channel_tree_t *channels )
|
||||
{
|
||||
assert(skel);
|
||||
|
||||
/* Setup */
|
||||
skel->id = strdup(id);
|
||||
skel->name = strdup(name);
|
||||
skel->channels = channels;
|
||||
if (channels) {
|
||||
skel->ch_save = epggrab_module_ch_save;
|
||||
skel->ch_add = epggrab_module_ch_add;
|
||||
skel->ch_mod = epggrab_module_ch_mod;
|
||||
skel->ch_rem = epggrab_module_ch_rem;
|
||||
}
|
||||
|
||||
/* Insert */
|
||||
assert(!epggrab_module_find_by_id(id));
|
||||
LIST_INSERT_HEAD(&epggrab_modules, skel, link);
|
||||
|
||||
return skel;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the parse
|
||||
*/
|
||||
void epggrab_module_parse
|
||||
( void *m, htsmsg_t *data )
|
||||
{
|
||||
time_t tm1, tm2;
|
||||
int save = 0;
|
||||
epggrab_stats_t stats;
|
||||
epggrab_module_int_t *mod = m;
|
||||
|
||||
/* Parse */
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
pthread_mutex_lock(&global_lock);
|
||||
time(&tm1);
|
||||
save |= mod->parse(mod, data, &stats);
|
||||
time(&tm2);
|
||||
if (save) epg_updated();
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
htsmsg_destroy(data);
|
||||
|
||||
/* Debug stats */
|
||||
tvhlog(LOG_INFO, mod->id, "parse took %d seconds", tm2 - tm1);
|
||||
tvhlog(LOG_INFO, mod->id, " channels tot=%5d new=%5d mod=%5d",
|
||||
stats.channels.total, stats.channels.created,
|
||||
stats.channels.modified);
|
||||
tvhlog(LOG_INFO, mod->id, " brands tot=%5d new=%5d mod=%5d",
|
||||
stats.brands.total, stats.brands.created,
|
||||
stats.brands.modified);
|
||||
tvhlog(LOG_INFO, mod->id, " seasons tot=%5d new=%5d mod=%5d",
|
||||
stats.seasons.total, stats.seasons.created,
|
||||
stats.seasons.modified);
|
||||
tvhlog(LOG_INFO, mod->id, " episodes tot=%5d new=%5d mod=%5d",
|
||||
stats.episodes.total, stats.episodes.created,
|
||||
stats.episodes.modified);
|
||||
tvhlog(LOG_INFO, mod->id, " broadcasts tot=%5d new=%5d mod=%5d",
|
||||
stats.broadcasts.total, stats.broadcasts.created,
|
||||
stats.broadcasts.modified);
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Module channel routines
|
||||
* *************************************************************************/
|
||||
|
||||
void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch )
|
||||
{
|
||||
int i;
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_t *a;
|
||||
epggrab_module_t *mod = _m;
|
||||
|
||||
if (ch->name)
|
||||
htsmsg_add_str(m, "name", ch->name);
|
||||
if (ch->icon)
|
||||
htsmsg_add_str(m, "icon", ch->icon);
|
||||
if (ch->channel)
|
||||
htsmsg_add_u32(m, "channel", ch->channel->ch_id);
|
||||
if (ch->sid) {
|
||||
a = htsmsg_create_list();
|
||||
i = 0;
|
||||
while (ch->sid[i]) {
|
||||
htsmsg_add_u32(a, NULL, ch->sid[i]);
|
||||
i++;
|
||||
}
|
||||
htsmsg_add_msg(m, "sid", a);
|
||||
}
|
||||
if (ch->sname) {
|
||||
a = htsmsg_create_list();
|
||||
i = 0;
|
||||
while (ch->sname[i]) {
|
||||
htsmsg_add_str(a, NULL, ch->sname[i]);
|
||||
i++;
|
||||
}
|
||||
htsmsg_add_msg(m, "sname", a);
|
||||
}
|
||||
if (ch->number)
|
||||
htsmsg_add_u32(m, "number", ch->number);
|
||||
|
||||
hts_settings_save(m, "epggrab/%s/channels/%s", mod->id, ch->id);
|
||||
}
|
||||
|
||||
void epggrab_module_ch_add ( void *m, channel_t *ch )
|
||||
{
|
||||
epggrab_channel_t *egc;
|
||||
epggrab_module_int_t *mod = m;
|
||||
RB_FOREACH(egc, mod->channels, link) {
|
||||
if (epggrab_channel_link(egc, ch)) {
|
||||
if (mod->ch_save) mod->ch_save(mod, egc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_module_ch_rem ( void *m, channel_t *ch )
|
||||
{
|
||||
epggrab_channel_t *egc;
|
||||
epggrab_module_int_t *mod = m;
|
||||
RB_FOREACH(egc, mod->channels, link) {
|
||||
if (egc->channel == ch) {
|
||||
egc->channel = NULL;
|
||||
if (mod->ch_save) mod->ch_save(mod, egc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_module_ch_mod ( void *mod, channel_t *ch )
|
||||
{
|
||||
return epggrab_module_ch_add(mod, ch);
|
||||
}
|
||||
|
||||
static void _epggrab_module_channel_load
|
||||
( epggrab_module_t *mod, htsmsg_t *m, const char *id )
|
||||
{
|
||||
int save = 0, i;
|
||||
const char *str;
|
||||
uint32_t u32;
|
||||
htsmsg_t *a;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
epggrab_channel_t *ch = epggrab_channel_find(mod->channels, id, 1, &save);
|
||||
|
||||
if ((str = htsmsg_get_str(m, "name")))
|
||||
ch->name = strdup(str);
|
||||
if ((str = htsmsg_get_str(m, "icon")))
|
||||
ch->icon = strdup(str);
|
||||
if ((a = htsmsg_get_list(m, "sid"))) {
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) i++;
|
||||
if (i) {
|
||||
ch->sid = calloc(i+1, sizeof(uint16_t));
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) {
|
||||
ch->sid[i++] = (uint16_t)f->hmf_s64;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((a = htsmsg_get_list(m, "sname"))) {
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) i++;
|
||||
if (i) {
|
||||
ch->sname = calloc(i+1, sizeof(char*));
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) {
|
||||
ch->sname[i++] = strdup(f->hmf_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!htsmsg_get_u32(m, "number", &u32))
|
||||
ch->number = u32;
|
||||
|
||||
if (!htsmsg_get_u32(m, "channel", &u32))
|
||||
ch->channel = channel_find_by_identifier(u32);
|
||||
}
|
||||
|
||||
void epggrab_module_channels_load ( epggrab_module_t *mod )
|
||||
{
|
||||
htsmsg_t *m, *e;
|
||||
htsmsg_field_t *f;
|
||||
if (!mod || !mod->channels) return;
|
||||
if ((m = hts_settings_load("epggrab/%s/channels", mod->id))) {
|
||||
HTSMSG_FOREACH(f, m) {
|
||||
if ((e = htsmsg_get_map_by_field(f)))
|
||||
_epggrab_module_channel_load(mod, e, f->hmf_name);
|
||||
}
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Internal module routines
|
||||
* *************************************************************************/
|
||||
|
||||
epggrab_module_int_t *epggrab_module_int_create
|
||||
( epggrab_module_int_t *skel,
|
||||
const char *id, const char *name, const char *path,
|
||||
char* (*grab) (void*m),
|
||||
int (*parse) (void *m, htsmsg_t *data, epggrab_stats_t *sta),
|
||||
htsmsg_t* (*trans) (void *mod, char *data),
|
||||
epggrab_channel_tree_t *channels )
|
||||
{
|
||||
/* Allocate data */
|
||||
if (!skel) skel = calloc(1, sizeof(epggrab_module_int_t));
|
||||
|
||||
/* Pass through */
|
||||
epggrab_module_create((epggrab_module_t*)skel, id, name, channels);
|
||||
|
||||
/* Int data */
|
||||
skel->type = EPGGRAB_INT;
|
||||
skel->path = strdup(path);
|
||||
skel->channels = channels;
|
||||
skel->grab = grab ?: epggrab_module_grab_spawn;
|
||||
skel->trans = trans ?: epggrab_module_trans_xml;
|
||||
skel->parse = parse;
|
||||
|
||||
return skel;
|
||||
}
|
||||
|
||||
char *epggrab_module_grab_spawn ( void *m )
|
||||
{
|
||||
int outlen;
|
||||
char *outbuf;
|
||||
epggrab_module_int_t *mod = m;
|
||||
|
||||
/* Debug */
|
||||
tvhlog(LOG_INFO, mod->id, "grab %s", mod->path);
|
||||
|
||||
/* Grab */
|
||||
outlen = spawn_and_store_stdout(mod->path, NULL, &outbuf);
|
||||
if ( outlen < 1 ) {
|
||||
tvhlog(LOG_ERR, "pyepg", "no output detected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return outbuf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
htsmsg_t *epggrab_module_trans_xml ( void *m, char *c )
|
||||
{
|
||||
htsmsg_t *ret;
|
||||
char errbuf[100];
|
||||
|
||||
if (!c) return NULL;
|
||||
|
||||
/* Extract */
|
||||
ret = htsmsg_xml_deserialize(c, errbuf, sizeof(errbuf));
|
||||
if (!ret)
|
||||
tvhlog(LOG_ERR, "pyepg", "htsmsg_xml_deserialize error %s", errbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* External module routines
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* Socket handler
|
||||
*/
|
||||
static void _epggrab_socket_handler ( epggrab_module_ext_t *mod, int s )
|
||||
{
|
||||
size_t outlen;
|
||||
char *outbuf;
|
||||
time_t tm1, tm2;
|
||||
htsmsg_t *data = NULL;
|
||||
|
||||
/* Grab/Translate */
|
||||
time(&tm1);
|
||||
outlen = file_readall(s, &outbuf);
|
||||
if (outlen) data = mod->trans(mod, outbuf);
|
||||
time(&tm2);
|
||||
|
||||
/* Process */
|
||||
if ( data ) {
|
||||
tvhlog(LOG_INFO, mod->id, "grab took %d seconds", tm2 - tm1);
|
||||
epggrab_module_parse(mod, data);
|
||||
|
||||
/* Failed */
|
||||
} else {
|
||||
tvhlog(LOG_ERR, mod->id, "failed to read data");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* External (socket) grab thread
|
||||
*/
|
||||
static void *_epggrab_socket_thread ( void *p )
|
||||
{
|
||||
int s;
|
||||
epggrab_module_ext_t *mod = (epggrab_module_ext_t*)p;
|
||||
tvhlog(LOG_INFO, mod->id, "external socket enabled");
|
||||
|
||||
while ( mod->enabled && mod->sock ) {
|
||||
tvhlog(LOG_DEBUG, mod->id, "waiting for connection");
|
||||
s = accept(mod->sock, NULL, NULL);
|
||||
if (s <= 0) continue;
|
||||
tvhlog(LOG_DEBUG, mod->id, "got connection %d", s);
|
||||
_epggrab_socket_handler(mod, s);
|
||||
}
|
||||
tvhlog(LOG_DEBUG, mod->id, "terminated");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable socket module
|
||||
*/
|
||||
int epggrab_module_enable_socket ( void *m, uint8_t e )
|
||||
{
|
||||
pthread_t tid;
|
||||
pthread_attr_t tattr;
|
||||
struct sockaddr_un addr;
|
||||
epggrab_module_ext_t *mod = (epggrab_module_ext_t*)m;
|
||||
assert(mod->type == EPGGRAB_EXT);
|
||||
|
||||
/* Ignore */
|
||||
if ( mod->enabled == e ) return 0;
|
||||
|
||||
/* Disable */
|
||||
if (!e) {
|
||||
shutdown(mod->sock, SHUT_RDWR);
|
||||
close(mod->sock);
|
||||
unlink(mod->path);
|
||||
mod->sock = 0;
|
||||
|
||||
/* Enable */
|
||||
} else {
|
||||
unlink(mod->path); // just in case!
|
||||
|
||||
mod->sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
assert(mod->sock);
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, mod->path, 100);
|
||||
if ( bind(mod->sock, (struct sockaddr*)&addr,
|
||||
sizeof(struct sockaddr_un)) != 0 ) {
|
||||
tvhlog(LOG_ERR, mod->id, "failed to bind socket");
|
||||
close(mod->sock);
|
||||
mod->sock = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( listen(mod->sock, 5) != 0 ) {
|
||||
tvhlog(LOG_ERR, mod->id, "failed to listen on socket");
|
||||
close(mod->sock);
|
||||
mod->sock = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tvhlog(LOG_DEBUG, mod->id, "starting socket thread");
|
||||
pthread_attr_init(&tattr);
|
||||
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&tid, &tattr, _epggrab_socket_thread, mod);
|
||||
}
|
||||
mod->enabled = e;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a module
|
||||
*/
|
||||
epggrab_module_ext_t *epggrab_module_ext_create
|
||||
( epggrab_module_ext_t *skel,
|
||||
const char *id, const char *name, const char *sockid,
|
||||
int (*parse) (void *m, htsmsg_t *data, epggrab_stats_t *sta),
|
||||
htsmsg_t* (*trans) (void *mod, char *data),
|
||||
epggrab_channel_tree_t *channels )
|
||||
{
|
||||
char path[512];
|
||||
|
||||
/* Allocate data */
|
||||
if (!skel) skel = calloc(1, sizeof(epggrab_module_ext_t));
|
||||
|
||||
/* Pass through */
|
||||
snprintf(path, 512, "%s/epggrab/%s.sock",
|
||||
hts_settings_get_root(), sockid);
|
||||
epggrab_module_int_create((epggrab_module_int_t*)skel,
|
||||
id, name, path,
|
||||
NULL, parse, trans,
|
||||
channels);
|
||||
|
||||
/* Local */
|
||||
skel->type = EPGGRAB_EXT;
|
||||
skel->enable = epggrab_module_enable_socket;
|
||||
|
||||
return skel;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* OTA module functions
|
||||
* *************************************************************************/
|
||||
|
||||
epggrab_module_ota_t *epggrab_module_ota_create
|
||||
( epggrab_module_ota_t *skel,
|
||||
const char *id, const char *name,
|
||||
void (*start) (epggrab_module_ota_t*m,
|
||||
struct th_dvb_mux_instance *tdmi),
|
||||
int (*enable) (void *m, uint8_t e ),
|
||||
epggrab_channel_tree_t *channels )
|
||||
{
|
||||
if (!skel) skel = calloc(1, sizeof(epggrab_module_ota_t));
|
||||
|
||||
/* Pass through */
|
||||
epggrab_module_create((epggrab_module_t*)skel, id, name, channels);
|
||||
|
||||
/* Setup */
|
||||
skel->type = EPGGRAB_OTA;
|
||||
skel->enable = enable;
|
||||
skel->start = start;
|
||||
TAILQ_INIT(&skel->muxes);
|
||||
|
||||
return skel;
|
||||
}
|
||||
|
|
@ -23,9 +23,38 @@
|
|||
#include "dvb/dvb_support.h"
|
||||
#include "service.h"
|
||||
#include "epg.h"
|
||||
#include "epggrab/eit.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/private.h"
|
||||
|
||||
static epggrab_module_t _eit_mod;
|
||||
/* ************************************************************************
|
||||
* Status handling
|
||||
* ***********************************************************************/
|
||||
|
||||
typedef struct eit_status
|
||||
{
|
||||
int tid;
|
||||
int sec;
|
||||
} eit_status_t;
|
||||
|
||||
/* ************************************************************************
|
||||
* Diagnostics
|
||||
* ***********************************************************************/
|
||||
|
||||
// Dump a descriptor tag for debug (looking for new tags etc...)
|
||||
static void _eit_dtag_dump ( uint8_t dtag, uint8_t dlen, uint8_t *buf )
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
char tmp[100];
|
||||
tvhlog(LOG_DEBUG, "eit", " dtag 0x%02X len %d", dtag, dlen);
|
||||
while (i < dlen) {
|
||||
j += sprintf(tmp+j, "%02X ", buf[i]);
|
||||
i++;
|
||||
if ((i % 8) == 0 || !dlen) {
|
||||
tvhlog(LOG_DEBUG, "eit", " %s", tmp);
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* Processing
|
||||
|
@ -112,12 +141,13 @@ static int _eit_callback
|
|||
( th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
|
||||
uint8_t tableid, void *opaque )
|
||||
{
|
||||
th_dvb_mux_instance_t *otdmi = tdmi;
|
||||
epggrab_ota_mux_t *ota = (epggrab_ota_mux_t*)opaque;
|
||||
th_dvb_adapter_t *tda;
|
||||
service_t *svc;
|
||||
channel_t *ch;
|
||||
epg_broadcast_t *ebc;
|
||||
epg_episode_t *ee;
|
||||
eit_status_t *sta;
|
||||
int resched = 0, save = 0, save2 = 0, dllen, dtag, dlen;
|
||||
uint16_t tsid, sid, eid;
|
||||
uint8_t bw, hd, ws, ad, ds, st;
|
||||
|
@ -129,14 +159,26 @@ static int _eit_callback
|
|||
char desc[5000];
|
||||
htsmsg_t *extra;
|
||||
|
||||
/* Disabled */
|
||||
if(!_eit_mod.enabled) return 0;
|
||||
|
||||
/* Invalid */
|
||||
if(tableid < 0x4e || tableid > 0x6f || len < 11)
|
||||
return -1;
|
||||
|
||||
/* Skip */
|
||||
/* Already complete */
|
||||
if (epggrab_ota_is_complete(ota)) return 0;
|
||||
|
||||
/* Started */
|
||||
sta = ota->status;
|
||||
if (epggrab_ota_begin(ota)) {
|
||||
sta->tid = tableid;
|
||||
sta->sec = ptr[3];
|
||||
|
||||
/* Complete */
|
||||
} else if (sta->tid == tableid && sta->sec == ptr[3]) {
|
||||
epggrab_ota_complete(ota);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Don't process */
|
||||
if((ptr[2] & 1) == 0)
|
||||
return 0;
|
||||
|
||||
|
@ -159,15 +201,19 @@ static int _eit_callback
|
|||
svc = dvb_transport_find(tdmi, sid, 0, NULL);
|
||||
if (!svc || !svc->s_enabled || !(ch = svc->s_ch)) return 0;
|
||||
|
||||
|
||||
/* Ignore (disabled) */
|
||||
// TODO: should this be altered?
|
||||
if (!svc->s_dvb_eit_enable) return 0;
|
||||
|
||||
/* Register interest */
|
||||
/* Register as interesting */
|
||||
// TODO: do we want to register for now/next?
|
||||
// TODO: want should the intervals be?
|
||||
if (tableid < 0x50)
|
||||
epggrab_ota_register(&_eit_mod, otdmi, 20, 0);
|
||||
epggrab_ota_register(ota, 20, 0);
|
||||
else
|
||||
epggrab_ota_register(&_eit_mod, otdmi, 40, 0);
|
||||
|
||||
epggrab_ota_register(ota, 40, 0);
|
||||
|
||||
/* Up to date */
|
||||
#if TODO_INCLUDE_EIT_VER_CHECK
|
||||
ver = ptr[2] >> 1 & 0x1f;
|
||||
|
@ -198,7 +244,7 @@ static int _eit_callback
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Mark re-schedule detect */
|
||||
/* Mark re-schedule detect (only now/next) */
|
||||
if (save2 && tableid < 0x50) resched = 1;
|
||||
|
||||
/* Process tags */
|
||||
|
@ -298,6 +344,7 @@ static int _eit_callback
|
|||
|
||||
/* Ignore */
|
||||
default:
|
||||
_eit_dtag_dump(dtag, dlen, ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -346,9 +393,8 @@ static int _eit_callback
|
|||
}
|
||||
|
||||
/* Update EPG */
|
||||
if (resched) tvhlog(LOG_DEBUG, "eit", "alert grabbers to resched");
|
||||
// TODO: handle the reschedule
|
||||
if (save) epg_updated();
|
||||
if (resched) epggrab_resched();
|
||||
if (save) epg_updated();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -357,19 +403,21 @@ static int _eit_callback
|
|||
* Module Setup
|
||||
* ***********************************************************************/
|
||||
|
||||
static void _eit_tune ( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi )
|
||||
static void _eit_start
|
||||
( epggrab_module_ota_t *m, th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
if (_eit_mod.enabled)
|
||||
tdt_add(tdmi, NULL, _eit_callback, NULL, "eit", TDT_CRC, 0x12, NULL);
|
||||
epggrab_ota_mux_t *ota;
|
||||
if (!m->enabled) return;
|
||||
if (!(ota = epggrab_ota_create(m, tdmi))) return;
|
||||
if (!ota->status)
|
||||
ota->status = calloc(1, sizeof(eit_status_t));
|
||||
tdt_add(tdmi, NULL, _eit_callback, ota, "eit", TDT_CRC, 0x12, NULL);
|
||||
}
|
||||
|
||||
void eit_init ( epggrab_module_list_t *list )
|
||||
void eit_init ( void )
|
||||
{
|
||||
_eit_mod.id = strdup("eit");
|
||||
_eit_mod.name = strdup("EIT: On-Air Grabber");
|
||||
_eit_mod.tune = _eit_tune;
|
||||
*((uint8_t*)&_eit_mod.flags) = EPGGRAB_MODULE_OTA;
|
||||
LIST_INSERT_HEAD(list, &_eit_mod, link);
|
||||
epggrab_module_ota_create(NULL, "eit", "EIT: DVB Grabber",
|
||||
_eit_start, NULL, NULL);
|
||||
}
|
||||
|
||||
void eit_load ( void )
|
|
@ -25,8 +25,8 @@
|
|||
#include "channels.h"
|
||||
#include "huffman.h"
|
||||
#include "epg.h"
|
||||
#include "epggrab/ota.h"
|
||||
#include "epggrab/opentv.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/private.h"
|
||||
#include "subscriptions.h"
|
||||
#include "streaming.h"
|
||||
#include "service.h"
|
||||
|
@ -35,31 +35,82 @@
|
|||
|
||||
static epggrab_channel_tree_t _opentv_channels;
|
||||
|
||||
static void _opentv_tune
|
||||
( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi );
|
||||
static int _opentv_enable
|
||||
( epggrab_module_t *m, uint8_t e );
|
||||
|
||||
/* ************************************************************************
|
||||
* Data structures
|
||||
* Status monitoring
|
||||
* ***********************************************************************/
|
||||
|
||||
#define OPENTV_SCAN_MAX 600 // 10min max scan period
|
||||
#define OPENTV_SCAN_PER 3600 // 1hour interval
|
||||
/* Internal event structure */
|
||||
typedef struct opentv_event
|
||||
{
|
||||
RB_ENTRY(opentv_event) ev_link; ///< List of partial events
|
||||
uint16_t cid; ///< Channel ID
|
||||
uint16_t eid; ///< Events ID
|
||||
time_t start; ///< Start time
|
||||
time_t stop; ///< Event stop time
|
||||
char *title; ///< Event title
|
||||
char *summary; ///< Event summary
|
||||
char *desc; ///< Event description
|
||||
uint8_t cat; ///< Event category
|
||||
uint16_t series; ///< Series (link) reference
|
||||
|
||||
uint8_t type; ///< 0x1=title, 0x2=summary
|
||||
} opentv_event_t;
|
||||
|
||||
/* Data carousel status */
|
||||
/* PID status (for event PIDs) */
|
||||
typedef struct opentv_pid
|
||||
{
|
||||
LIST_ENTRY(opentv_pid) link;
|
||||
int pid;
|
||||
enum {
|
||||
OPENTV_PID_INIT,
|
||||
OPENTV_PID_STARTED,
|
||||
OPENTV_PID_COMPLETE
|
||||
} state;
|
||||
uint8_t start[20];
|
||||
} opentv_pid_t;
|
||||
|
||||
/* Scan status */
|
||||
typedef struct opentv_status
|
||||
{
|
||||
LIST_ENTRY(opentv_status) link;
|
||||
int pid;
|
||||
enum {
|
||||
OPENTV_STA_INIT,
|
||||
OPENTV_STA_STARTED,
|
||||
OPENTV_STA_COMPLETE
|
||||
} status;
|
||||
uint8_t start[20];
|
||||
int begbat;
|
||||
int endbat;
|
||||
LIST_HEAD(, opentv_pid) pids;
|
||||
RB_HEAD(, opentv_event) events;
|
||||
} opentv_status_t;
|
||||
|
||||
/* Get a pid entry */
|
||||
static opentv_pid_t *_opentv_status_get_pid
|
||||
( opentv_status_t *sta, int pid )
|
||||
{
|
||||
opentv_pid_t *p;
|
||||
LIST_FOREACH(p, &sta->pids, link) {
|
||||
if (p->pid == pid) break;
|
||||
}
|
||||
if (!p) {
|
||||
p = calloc(1, sizeof(opentv_pid_t));
|
||||
p->pid = pid;
|
||||
LIST_INSERT_HEAD(&sta->pids, p, link);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Clear events */
|
||||
static void _opentv_status_remove_events ( opentv_status_t *sta )
|
||||
{
|
||||
opentv_event_t *ev;
|
||||
while ((ev = RB_FIRST(&sta->events))) {
|
||||
RB_REMOVE(&sta->events, ev, ev_link);
|
||||
if (ev->title) free(ev->title);
|
||||
if (ev->summary) free(ev->summary);
|
||||
if (ev->desc) free(ev->desc);
|
||||
free(ev);
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* Module structure
|
||||
* ***********************************************************************/
|
||||
|
||||
/* Huffman dictionary */
|
||||
typedef struct opentv_dict
|
||||
{
|
||||
|
@ -71,7 +122,7 @@ typedef struct opentv_dict
|
|||
/* Provider configuration */
|
||||
typedef struct opentv_module_t
|
||||
{
|
||||
epggrab_module_t ; ///< Base struct
|
||||
epggrab_module_ota_t ; ///< Base struct
|
||||
|
||||
int nid;
|
||||
int tsid;
|
||||
|
@ -80,10 +131,6 @@ typedef struct opentv_module_t
|
|||
int *title;
|
||||
int *summary;
|
||||
opentv_dict_t *dict;
|
||||
|
||||
int endbat;
|
||||
int begbat;
|
||||
LIST_HEAD(, opentv_status) status;
|
||||
|
||||
} opentv_module_t;
|
||||
|
||||
|
@ -104,151 +151,6 @@ static opentv_dict_t *_opentv_dict_find ( const char *id )
|
|||
return RB_FIND(&_opentv_dicts, &skel, h_link, _dict_cmp);
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* Module functions
|
||||
* ***********************************************************************/
|
||||
|
||||
static opentv_status_t *_opentv_module_get_status
|
||||
( opentv_module_t *mod, int pid )
|
||||
{
|
||||
opentv_status_t *sta;
|
||||
LIST_FOREACH(sta, &mod->status, link)
|
||||
if (sta->pid == pid) break;
|
||||
if (!sta) {
|
||||
sta = calloc(1, sizeof(opentv_status_t));
|
||||
sta->pid = pid;
|
||||
LIST_INSERT_HEAD(&mod->status, sta, link);
|
||||
}
|
||||
return sta;
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* Configuration loading
|
||||
* ***********************************************************************/
|
||||
|
||||
static int* _pid_list_to_array ( htsmsg_t *m, opentv_module_t *mod )
|
||||
{
|
||||
int i = 1;
|
||||
int *ret;
|
||||
htsmsg_field_t *f;
|
||||
HTSMSG_FOREACH(f, m)
|
||||
if (f->hmf_s64) i++;
|
||||
ret = calloc(i, sizeof(int));
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, m)
|
||||
if (f->hmf_s64) {
|
||||
ret[i] = (int)f->hmf_s64;
|
||||
if (mod) (void)_opentv_module_get_status(mod, ret[i]);
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _opentv_dict_load_one ( const char *id, htsmsg_t *m )
|
||||
{
|
||||
opentv_dict_t *dict = calloc(1, sizeof(opentv_dict_t));
|
||||
dict->id = (char*)id;
|
||||
if (RB_INSERT_SORTED(&_opentv_dicts, dict, h_link, _dict_cmp)) {
|
||||
tvhlog(LOG_DEBUG, "opentv", "ignore duplicate dictionary %s", id);
|
||||
free(dict);
|
||||
return 0;
|
||||
} else {
|
||||
dict->codes = huffman_tree_build(m);
|
||||
if (!dict->codes) {
|
||||
RB_REMOVE(&_opentv_dicts, dict, h_link);
|
||||
free(dict);
|
||||
return -1;
|
||||
} else {
|
||||
dict->id = strdup(id);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _opentv_dict_load ( htsmsg_t *m )
|
||||
{
|
||||
int r;
|
||||
htsmsg_t *e;
|
||||
htsmsg_field_t *f;
|
||||
HTSMSG_FOREACH(f, m) {
|
||||
if ((e = htsmsg_get_list(m, f->hmf_name))) {
|
||||
if ((r = _opentv_dict_load_one(f->hmf_name, e))) {
|
||||
if (r > 0)
|
||||
tvhlog(LOG_INFO, "opentv", "dictionary %s loaded", f->hmf_name);
|
||||
else
|
||||
tvhlog(LOG_WARNING, "opentv", "dictionary %s failed", f->hmf_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
static int _opentv_prov_load_one
|
||||
( const char *id, htsmsg_t *m, epggrab_module_list_t *list )
|
||||
{
|
||||
char buf[100];
|
||||
htsmsg_t *cl, *tl, *sl;
|
||||
uint32_t tsid, sid, nid;
|
||||
const char *str, *name;
|
||||
opentv_dict_t *dict;
|
||||
static opentv_module_t *mod = NULL;
|
||||
|
||||
/* Check config */
|
||||
if (!(name = htsmsg_get_str(m, "name"))) return -1;
|
||||
if (!(str = htsmsg_get_str(m, "dict"))) return -1;
|
||||
if (!(dict = _opentv_dict_find(str))) return -1;
|
||||
if (!(cl = htsmsg_get_list(m, "channel"))) return -1;
|
||||
if (!(tl = htsmsg_get_list(m, "title"))) return -1;
|
||||
if (!(sl = htsmsg_get_list(m, "summary"))) return -1;
|
||||
if (htsmsg_get_u32(m, "nid", &nid)) return -1;
|
||||
if (htsmsg_get_u32(m, "tsid", &tsid)) return -1;
|
||||
if (htsmsg_get_u32(m, "sid", &sid)) return -1;
|
||||
|
||||
/* Exists */
|
||||
sprintf(buf, "opentv-%s", id);
|
||||
if (epggrab_module_find_by_id(buf)) return 0;
|
||||
|
||||
/* Create module */
|
||||
mod = calloc(1, sizeof(opentv_module_t));
|
||||
mod->id = strdup(buf);
|
||||
sprintf(buf, "OpenTV: %s", name);
|
||||
mod->name = strdup(buf);
|
||||
mod->enable = _opentv_enable;
|
||||
mod->tune = _opentv_tune;
|
||||
mod->channels = &_opentv_channels;
|
||||
*((uint8_t*)&mod->flags) = EPGGRAB_MODULE_OTA;
|
||||
LIST_INSERT_HEAD(list, ((epggrab_module_t*)mod), link);
|
||||
|
||||
/* Add provider details */
|
||||
mod->dict = dict;
|
||||
mod->nid = nid;
|
||||
mod->tsid = tsid;
|
||||
mod->sid = sid;
|
||||
mod->channel = _pid_list_to_array(cl, NULL);
|
||||
mod->title = _pid_list_to_array(tl, mod);
|
||||
mod->summary = _pid_list_to_array(sl, mod);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _opentv_prov_load ( htsmsg_t *m, epggrab_module_list_t *list )
|
||||
{
|
||||
int r;
|
||||
htsmsg_t *e;
|
||||
htsmsg_field_t *f;
|
||||
HTSMSG_FOREACH(f, m) {
|
||||
if ((e = htsmsg_get_map_by_field(f))) {
|
||||
if ((r = _opentv_prov_load_one(f->hmf_name, e, list))) {
|
||||
if (r > 0)
|
||||
tvhlog(LOG_INFO, "opentv", "provider %s loaded", f->hmf_name);
|
||||
else
|
||||
tvhlog(LOG_WARNING, "opentv", "provider %s failed", f->hmf_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* EPG Object wrappers
|
||||
* ***********************************************************************/
|
||||
|
@ -258,7 +160,7 @@ static epggrab_channel_t *_opentv_find_epggrab_channel
|
|||
{
|
||||
char chid[32];
|
||||
sprintf(chid, "%s-%d", mod->id, cid);
|
||||
return epggrab_module_channel_find((epggrab_module_t*)mod, chid, create, save);
|
||||
return epggrab_channel_find(&_opentv_channels, chid, create, save);
|
||||
}
|
||||
|
||||
static epg_season_t *_opentv_find_season
|
||||
|
@ -303,26 +205,6 @@ static channel_t *_opentv_find_channel ( int tsid, int sid )
|
|||
#define OPENTV_TITLE 0x01
|
||||
#define OPENTV_SUMMARY 0x02
|
||||
|
||||
/* Internal event structure */
|
||||
typedef struct opentv_event
|
||||
{
|
||||
RB_ENTRY(opentv_event) ev_link; ///< List of partial events
|
||||
uint16_t cid; ///< Channel ID
|
||||
uint16_t eid; ///< Events ID
|
||||
time_t start; ///< Start time
|
||||
time_t stop; ///< Event stop time
|
||||
char *title; ///< Event title
|
||||
char *summary; ///< Event summary
|
||||
char *desc; ///< Event description
|
||||
uint8_t cat; ///< Event category
|
||||
uint16_t series; ///< Series (link) reference
|
||||
|
||||
uint8_t type; ///< 0x1=title, 0x2=summary
|
||||
} opentv_event_t;
|
||||
|
||||
/* List of partial events */
|
||||
RB_HEAD(, opentv_event) _opentv_events;
|
||||
|
||||
/* Event list comparator */
|
||||
static int _ev_cmp ( void *_a, void *_b )
|
||||
{
|
||||
|
@ -399,7 +281,8 @@ static int _opentv_parse_event_record
|
|||
|
||||
/* Parse a specific event */
|
||||
static int _opentv_parse_event
|
||||
( opentv_module_t *prov, uint8_t *buf, int len, int cid, time_t mjd,
|
||||
( opentv_module_t *prov, opentv_status_t *sta,
|
||||
uint8_t *buf, int len, int cid, time_t mjd,
|
||||
opentv_event_t *ev, int type )
|
||||
{
|
||||
int slen = ((int)buf[2] & 0xf << 8) | buf[3];
|
||||
|
@ -410,9 +293,9 @@ static int _opentv_parse_event
|
|||
ev->eid = ((uint16_t)buf[0] << 8) | buf[1];
|
||||
|
||||
/* Get existing summary */
|
||||
e = RB_FIND(&_opentv_events, ev, ev_link, _ev_cmp);
|
||||
e = RB_FIND(&sta->events, ev, ev_link, _ev_cmp);
|
||||
if (e) {
|
||||
RB_REMOVE(&_opentv_events, e, ev_link);
|
||||
RB_REMOVE(&sta->events, e, ev_link);
|
||||
memcpy(ev, e, sizeof(opentv_event_t));
|
||||
free(e);
|
||||
}
|
||||
|
@ -427,7 +310,8 @@ static int _opentv_parse_event
|
|||
|
||||
/* Parse an event section */
|
||||
static int _opentv_parse_event_section
|
||||
( opentv_module_t *mod, uint8_t *buf, int len, int type )
|
||||
( opentv_module_t *mod, opentv_status_t *sta,
|
||||
uint8_t *buf, int len, int type )
|
||||
{
|
||||
int i, cid, save = 0;
|
||||
time_t mjd;
|
||||
|
@ -452,7 +336,7 @@ static int _opentv_parse_event_section
|
|||
i = 7;
|
||||
while (i < len) {
|
||||
memset(&ev, 0, sizeof(opentv_event_t));
|
||||
i += _opentv_parse_event(mod, buf+i, len-i, cid, mjd, &ev, type);
|
||||
i += _opentv_parse_event(mod, sta, buf+i, len-i, cid, mjd, &ev, type);
|
||||
|
||||
/* Find broadcast */
|
||||
if (ev.type & OPENTV_TITLE) {
|
||||
|
@ -465,7 +349,7 @@ static int _opentv_parse_event_section
|
|||
if (!ebc) {
|
||||
opentv_event_t *skel = malloc(sizeof(opentv_event_t));
|
||||
memcpy(skel, &ev, sizeof(opentv_event_t));
|
||||
assert(!RB_INSERT_SORTED(&_opentv_events, skel, ev_link, _ev_cmp));
|
||||
assert(!RB_INSERT_SORTED(&sta->events, skel, ev_link, _ev_cmp));
|
||||
continue; // don't want to free() anything
|
||||
}
|
||||
}
|
||||
|
@ -561,7 +445,7 @@ static int _opentv_parse_ts_desc
|
|||
}
|
||||
|
||||
static int _opentv_bat_section
|
||||
( opentv_module_t *mod, uint8_t *buf, int len )
|
||||
( opentv_module_t *mod, opentv_status_t *sta, uint8_t *buf, int len )
|
||||
{
|
||||
int i, r;
|
||||
int bdlen, tllen, tdlen;
|
||||
|
@ -579,10 +463,10 @@ static int _opentv_bat_section
|
|||
// Note: this is NOT the most robust of approaches, but it works
|
||||
// most of the time
|
||||
i = 0x80000000 | (bid << 8) | sec;
|
||||
if (!mod->begbat) {
|
||||
mod->begbat = i;
|
||||
} else if (mod->begbat == i) {
|
||||
mod->endbat = 1;
|
||||
if (!sta->begbat) {
|
||||
sta->begbat = i;
|
||||
} else if (sta->begbat == i) {
|
||||
sta->endbat = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -621,71 +505,58 @@ static int _opentv_bat_section
|
|||
* Table Callbacks
|
||||
* ***********************************************************************/
|
||||
|
||||
static opentv_module_t *_opentv_table_callback
|
||||
static epggrab_ota_mux_t *_opentv_event_callback
|
||||
( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p )
|
||||
{
|
||||
th_dvb_table_t *tdt = (th_dvb_table_t*)p;
|
||||
opentv_module_t *mod = (opentv_module_t*)tdt->tdt_opaque;
|
||||
epggrab_ota_mux_t *ota;
|
||||
opentv_status_t *sta;
|
||||
epggrab_ota_mux_t *ota = tdt->tdt_opaque;
|
||||
opentv_status_t *sta = ota->status;
|
||||
opentv_pid_t *pid;
|
||||
|
||||
/* Ignore BAT not complete */
|
||||
if (!mod->endbat) return NULL;
|
||||
|
||||
/* Ignore (not enough data) */
|
||||
if (len < 20) return NULL;
|
||||
|
||||
/* Register */
|
||||
ota = epggrab_ota_register((epggrab_module_t*)mod, tdmi,
|
||||
OPENTV_SCAN_MAX, OPENTV_SCAN_PER);
|
||||
if (!ota) return NULL;
|
||||
/* Ignore (don't have BAT) */
|
||||
if (!sta->endbat) return NULL;
|
||||
|
||||
/* Finished / Blocked */
|
||||
if (epggrab_ota_is_complete(ota)) return NULL;
|
||||
if (epggrab_ota_is_blocked(ota)) return NULL;
|
||||
|
||||
/* Begin (reset state) */
|
||||
if (epggrab_ota_begin(ota)) {
|
||||
opentv_event_t *ev;
|
||||
|
||||
/* Remove outstanding event data */
|
||||
while ((ev = RB_FIRST(&_opentv_events))) {
|
||||
RB_REMOVE(&_opentv_events, ev, ev_link);
|
||||
if (ev->title) free(ev->title);
|
||||
if (ev->desc) free(ev->desc);
|
||||
if (ev->summary) free(ev->summary);
|
||||
free(ev);
|
||||
}
|
||||
_opentv_status_remove_events(sta);
|
||||
|
||||
/* Reset status */
|
||||
LIST_FOREACH(sta, &mod->status, link)
|
||||
sta->status = OPENTV_STA_INIT;
|
||||
LIST_FOREACH(pid, &sta->pids, link)
|
||||
pid->state = OPENTV_PID_INIT;
|
||||
}
|
||||
|
||||
/* Insert/Find */
|
||||
sta = _opentv_module_get_status(mod, tdt->tdt_pid);
|
||||
pid = _opentv_status_get_pid(sta, tdt->tdt_pid);
|
||||
|
||||
/* Begin PID */
|
||||
if (sta->status == OPENTV_STA_INIT) {
|
||||
sta->status = OPENTV_STA_STARTED;
|
||||
memcpy(sta->start, buf, 20);
|
||||
return mod;
|
||||
if (pid->state == OPENTV_PID_INIT) {
|
||||
pid->state = OPENTV_PID_STARTED;
|
||||
memcpy(pid->start, buf, 20);
|
||||
return ota;
|
||||
|
||||
/* PID Already complete */
|
||||
} else if (sta->status == OPENTV_STA_COMPLETE) {
|
||||
} else if (pid->state == OPENTV_PID_COMPLETE) {
|
||||
return NULL;
|
||||
|
||||
/* End PID */
|
||||
} else if (!memcmp(sta->start, buf, 20)) {
|
||||
sta->status = OPENTV_STA_COMPLETE;
|
||||
} else if (!memcmp(pid->start, buf, 20)) {
|
||||
pid->state = OPENTV_PID_COMPLETE;
|
||||
|
||||
/* Check rest */
|
||||
LIST_FOREACH(sta, &mod->status, link)
|
||||
if (sta->status != OPENTV_STA_COMPLETE) return mod;
|
||||
LIST_FOREACH(pid, &sta->pids, link)
|
||||
if (pid->state != OPENTV_PID_COMPLETE) return ota;
|
||||
|
||||
/* PID in progress */
|
||||
} else {
|
||||
return mod;
|
||||
return ota;
|
||||
}
|
||||
|
||||
/* Mark complete */
|
||||
|
@ -697,106 +568,269 @@ static opentv_module_t *_opentv_table_callback
|
|||
static int _opentv_title_callback
|
||||
( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p )
|
||||
{
|
||||
opentv_module_t *mod = _opentv_table_callback(tdmi, buf, len, tid, p);
|
||||
if (mod)
|
||||
return _opentv_parse_event_section(mod, buf, len, OPENTV_TITLE);
|
||||
epggrab_ota_mux_t *ota = _opentv_event_callback(tdmi, buf, len, tid, p);
|
||||
if (ota)
|
||||
return _opentv_parse_event_section((opentv_module_t*)ota->grab,
|
||||
(opentv_status_t*)ota->status,
|
||||
buf, len, OPENTV_TITLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _opentv_summary_callback
|
||||
( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p )
|
||||
{
|
||||
opentv_module_t *mod = _opentv_table_callback(tdmi, buf, len, tid, p);
|
||||
if (mod)
|
||||
return _opentv_parse_event_section(mod, buf, len, OPENTV_SUMMARY);
|
||||
epggrab_ota_mux_t *ota = _opentv_event_callback(tdmi, buf, len, tid, p);
|
||||
if (ota)
|
||||
return _opentv_parse_event_section((opentv_module_t*)ota->grab,
|
||||
(opentv_status_t*)ota->status,
|
||||
buf, len, OPENTV_SUMMARY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _opentv_channel_callback
|
||||
( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p )
|
||||
{
|
||||
opentv_module_t *mod = (opentv_module_t*)p;
|
||||
if (!mod->endbat)
|
||||
return _opentv_bat_section(mod, buf, len);
|
||||
epggrab_ota_mux_t *ota = (epggrab_ota_mux_t*)p;
|
||||
opentv_status_t *sta = ota->status;
|
||||
if (!sta->endbat)
|
||||
return _opentv_bat_section((opentv_module_t*)ota->grab, sta, buf, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* Module callbacks
|
||||
* ***********************************************************************/
|
||||
|
||||
static void _opentv_ota_destroy ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
opentv_status_t *sta = ota->status;
|
||||
opentv_pid_t *pid;
|
||||
|
||||
/* Empty the events */
|
||||
_opentv_status_remove_events(sta);
|
||||
|
||||
/* Empty pids */
|
||||
while ((pid = LIST_FIRST(&sta->pids))) {
|
||||
LIST_REMOVE(pid, link);
|
||||
free(pid);
|
||||
}
|
||||
|
||||
/* Free the rest */
|
||||
free(sta);
|
||||
free(ota);
|
||||
}
|
||||
|
||||
static void _opentv_start
|
||||
( epggrab_module_ota_t *m, th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
int *t;
|
||||
struct dmx_sct_filter_params *fp;
|
||||
epggrab_ota_mux_t *ota;
|
||||
opentv_module_t *mod = (opentv_module_t*)m;
|
||||
opentv_status_t *sta;
|
||||
|
||||
/* Ignore */
|
||||
if (!m->enabled) return;
|
||||
if (mod->tsid != tdmi->tdmi_transport_stream_id) return;
|
||||
|
||||
/* Create link */
|
||||
if (!(ota = epggrab_ota_create(m, tdmi))) return;
|
||||
if (!ota->status) {
|
||||
ota->status = calloc(1, sizeof(opentv_status_t));
|
||||
ota->destroy = _opentv_ota_destroy;
|
||||
}
|
||||
sta = ota->status;
|
||||
sta->begbat = sta->endbat = 0;
|
||||
|
||||
/* Register (just in case we missed it on enable somehow) */
|
||||
epggrab_ota_register(ota, 600, 3600); // 10min scan every hour
|
||||
|
||||
/* Install tables */
|
||||
tvhlog(LOG_INFO, "opentv", "install provider %s tables", mod->id);
|
||||
|
||||
/* Channels */
|
||||
t = mod->channel;
|
||||
while (*t) {
|
||||
fp = dvb_fparams_alloc();
|
||||
fp->filter.filter[0] = 0x4a;
|
||||
fp->filter.mask[0] = 0xff;
|
||||
// TODO: what about 0x46 (service description)
|
||||
tdt_add(tdmi, fp, _opentv_channel_callback, ota,
|
||||
m->id, TDT_CRC, *t++, NULL);
|
||||
}
|
||||
|
||||
/* Titles */
|
||||
t = mod->title;
|
||||
while (*t) {
|
||||
fp = dvb_fparams_alloc();
|
||||
fp->filter.filter[0] = 0xa0;
|
||||
fp->filter.mask[0] = 0xfc;
|
||||
_opentv_status_get_pid(sta, *t);
|
||||
tdt_add(tdmi, fp, _opentv_title_callback, ota,
|
||||
m->id, TDT_CRC | TDT_TDT, *t++, NULL);
|
||||
}
|
||||
|
||||
/* Summaries */
|
||||
t = mod->summary;
|
||||
while (*t) {
|
||||
fp = dvb_fparams_alloc();
|
||||
fp->filter.filter[0] = 0xa8;
|
||||
fp->filter.mask[0] = 0xfc;
|
||||
_opentv_status_get_pid(sta, *t);
|
||||
tdt_add(tdmi, fp, _opentv_summary_callback, ota,
|
||||
m->id, TDT_CRC | TDT_TDT, *t++, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int _opentv_enable ( void *m, uint8_t e )
|
||||
{
|
||||
opentv_module_t *mod = (opentv_module_t*)m;
|
||||
|
||||
if (mod->enabled == e) return 0;
|
||||
mod->enabled = e;
|
||||
|
||||
/* Register interest */
|
||||
if (e) {
|
||||
epggrab_ota_create_and_register_by_id((epggrab_module_ota_t*)mod,
|
||||
mod->nid, mod->tsid,
|
||||
600, 3600);
|
||||
/* Remove all links */
|
||||
} else {
|
||||
epggrab_ota_destroy_by_module((epggrab_module_ota_t*)mod);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* Configuration
|
||||
* ***********************************************************************/
|
||||
|
||||
static int* _pid_list_to_array ( htsmsg_t *m )
|
||||
{
|
||||
int i = 1;
|
||||
int *ret;
|
||||
htsmsg_field_t *f;
|
||||
HTSMSG_FOREACH(f, m)
|
||||
if (f->hmf_s64) i++;
|
||||
ret = calloc(i, sizeof(int));
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, m)
|
||||
if (f->hmf_s64) {
|
||||
ret[i] = (int)f->hmf_s64;
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _opentv_dict_load_one ( const char *id, htsmsg_t *m )
|
||||
{
|
||||
opentv_dict_t *dict = calloc(1, sizeof(opentv_dict_t));
|
||||
dict->id = (char*)id;
|
||||
if (RB_INSERT_SORTED(&_opentv_dicts, dict, h_link, _dict_cmp)) {
|
||||
tvhlog(LOG_DEBUG, "opentv", "ignore duplicate dictionary %s", id);
|
||||
free(dict);
|
||||
return 0;
|
||||
} else {
|
||||
dict->codes = huffman_tree_build(m);
|
||||
if (!dict->codes) {
|
||||
RB_REMOVE(&_opentv_dicts, dict, h_link);
|
||||
free(dict);
|
||||
return -1;
|
||||
} else {
|
||||
dict->id = strdup(id);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _opentv_dict_load ( htsmsg_t *m )
|
||||
{
|
||||
int r;
|
||||
htsmsg_t *e;
|
||||
htsmsg_field_t *f;
|
||||
HTSMSG_FOREACH(f, m) {
|
||||
if ((e = htsmsg_get_list(m, f->hmf_name))) {
|
||||
if ((r = _opentv_dict_load_one(f->hmf_name, e))) {
|
||||
if (r > 0)
|
||||
tvhlog(LOG_INFO, "opentv", "dictionary %s loaded", f->hmf_name);
|
||||
else
|
||||
tvhlog(LOG_WARNING, "opentv", "dictionary %s failed", f->hmf_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
static int _opentv_prov_load_one ( const char *id, htsmsg_t *m )
|
||||
{
|
||||
char ibuf[100], nbuf[1000];
|
||||
htsmsg_t *cl, *tl, *sl;
|
||||
uint32_t tsid, sid, nid;
|
||||
const char *str, *name;
|
||||
opentv_dict_t *dict;
|
||||
opentv_module_t *mod;
|
||||
|
||||
/* Check config */
|
||||
if (!(name = htsmsg_get_str(m, "name"))) return -1;
|
||||
if (!(str = htsmsg_get_str(m, "dict"))) return -1;
|
||||
if (!(dict = _opentv_dict_find(str))) return -1;
|
||||
if (!(cl = htsmsg_get_list(m, "channel"))) return -1;
|
||||
if (!(tl = htsmsg_get_list(m, "title"))) return -1;
|
||||
if (!(sl = htsmsg_get_list(m, "summary"))) return -1;
|
||||
if (htsmsg_get_u32(m, "nid", &nid)) return -1;
|
||||
if (htsmsg_get_u32(m, "tsid", &tsid)) return -1;
|
||||
if (htsmsg_get_u32(m, "sid", &sid)) return -1;
|
||||
|
||||
/* Exists (we expect some duplicates due to config layout) */
|
||||
sprintf(ibuf, "opentv-%s", id);
|
||||
if (epggrab_module_find_by_id(ibuf)) return 0;
|
||||
|
||||
/* Create */
|
||||
sprintf(nbuf, "OpenTV: %s", name);
|
||||
mod = (opentv_module_t*)
|
||||
epggrab_module_ota_create(calloc(1, sizeof(opentv_module_t)),
|
||||
ibuf, nbuf,
|
||||
_opentv_start, _opentv_enable,
|
||||
NULL);
|
||||
|
||||
/* Add provider details */
|
||||
mod->dict = dict;
|
||||
mod->nid = nid;
|
||||
mod->tsid = tsid;
|
||||
mod->sid = sid;
|
||||
mod->channel = _pid_list_to_array(cl);
|
||||
mod->title = _pid_list_to_array(tl);
|
||||
mod->summary = _pid_list_to_array(sl);
|
||||
mod->channels = &_opentv_channels;
|
||||
mod->ch_rem = epggrab_module_ch_rem;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _opentv_prov_load ( htsmsg_t *m )
|
||||
{
|
||||
int r;
|
||||
htsmsg_t *e;
|
||||
htsmsg_field_t *f;
|
||||
HTSMSG_FOREACH(f, m) {
|
||||
if ((e = htsmsg_get_map_by_field(f))) {
|
||||
if ((r = _opentv_prov_load_one(f->hmf_name, e))) {
|
||||
if (r > 0)
|
||||
tvhlog(LOG_INFO, "opentv", "provider %s loaded", f->hmf_name);
|
||||
else
|
||||
tvhlog(LOG_WARNING, "opentv", "provider %s failed", f->hmf_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* Module Setup
|
||||
* ***********************************************************************/
|
||||
|
||||
static void _opentv_tune ( epggrab_module_t *m, th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
int *t;
|
||||
struct dmx_sct_filter_params *fp;
|
||||
opentv_module_t *mod = (opentv_module_t*)m;
|
||||
|
||||
/* Install tables */
|
||||
if (m->enabled && (mod->tsid == tdmi->tdmi_transport_stream_id)) {
|
||||
tvhlog(LOG_INFO, "opentv", "install provider %s tables", mod->id);
|
||||
|
||||
/* Channels */
|
||||
t = mod->channel;
|
||||
while (*t) {
|
||||
fp = dvb_fparams_alloc();
|
||||
fp->filter.filter[0] = 0x4a;
|
||||
fp->filter.mask[0] = 0xff;
|
||||
// TODO: what about 0x46 (service description)
|
||||
tdt_add(tdmi, fp, _opentv_channel_callback, mod,
|
||||
m->id, TDT_CRC, *t++, NULL);
|
||||
}
|
||||
|
||||
/* Titles */
|
||||
t = mod->title;
|
||||
while (*t) {
|
||||
fp = dvb_fparams_alloc();
|
||||
fp->filter.filter[0] = 0xa0;
|
||||
fp->filter.mask[0] = 0xfc;
|
||||
tdt_add(tdmi, fp, _opentv_title_callback, mod,
|
||||
m->id, TDT_CRC | TDT_TDT, *t++, NULL);
|
||||
}
|
||||
|
||||
/* Summaries */
|
||||
t = mod->summary;
|
||||
while (*t) {
|
||||
fp = dvb_fparams_alloc();
|
||||
fp->filter.filter[0] = 0xa8;
|
||||
fp->filter.mask[0] = 0xfc;
|
||||
tdt_add(tdmi, fp, _opentv_summary_callback, mod,
|
||||
m->id, TDT_CRC | TDT_TDT, *t++, NULL);
|
||||
}
|
||||
|
||||
/* Must rebuild the BAT */
|
||||
mod->begbat = mod->endbat = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int _opentv_enable ( epggrab_module_t *m, uint8_t e )
|
||||
{
|
||||
th_dvb_adapter_t *tda;
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
opentv_module_t *mod = (opentv_module_t*)m;
|
||||
|
||||
if (m->enabled == e) return 0;
|
||||
|
||||
m->enabled = e;
|
||||
|
||||
/* Find muxes and enable/disable */
|
||||
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
|
||||
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
|
||||
if (tdmi->tdmi_transport_stream_id != mod->tsid) continue;
|
||||
if (e) {
|
||||
epggrab_ota_register(m, tdmi, OPENTV_SCAN_MAX, OPENTV_SCAN_PER);
|
||||
} else {
|
||||
epggrab_ota_unregister_one(m, tdmi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void opentv_init ( epggrab_module_list_t *list )
|
||||
void opentv_init ( void )
|
||||
{
|
||||
htsmsg_t *m;
|
||||
const char *dr = tvheadend_dataroot();
|
||||
|
@ -810,9 +844,9 @@ void opentv_init ( epggrab_module_list_t *list )
|
|||
|
||||
/* Load providers */
|
||||
if ((m = hts_settings_load("epggrab/opentv/prov")))
|
||||
_opentv_prov_load(m, list);
|
||||
_opentv_prov_load(m);
|
||||
if ((m = hts_settings_load("%s/data/epggrab/opentv/prov", dr)))
|
||||
_opentv_prov_load(m, list);
|
||||
_opentv_prov_load(m);
|
||||
tvhlog(LOG_INFO, "opentv", "providers loaded");
|
||||
}
|
||||
|
|
@ -24,18 +24,17 @@
|
|||
#include "htsmsg_xml.h"
|
||||
#include "tvheadend.h"
|
||||
#include "spawn.h"
|
||||
#include "epg.h"
|
||||
#include "epggrab/pyepg.h"
|
||||
#include "epggrab/xmltv.h"
|
||||
#include "channels.h"
|
||||
#include "epg.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/private.h"
|
||||
|
||||
static epggrab_channel_tree_t _pyepg_channels;
|
||||
static epggrab_module_t *_pyepg_module;
|
||||
|
||||
static epggrab_channel_t *_pyepg_channel_find
|
||||
( const char *id, int create, int *save )
|
||||
{
|
||||
return epggrab_module_channel_find(_pyepg_module, id, create, save);
|
||||
return epggrab_channel_find(&_pyepg_channels, id, create, save);
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
|
@ -69,7 +68,7 @@ static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats )
|
|||
const char *str;
|
||||
uint32_t u32;
|
||||
const char *sname[11];
|
||||
uint16_t sid[10];
|
||||
uint16_t sid[11];
|
||||
int sid_idx = 0, sname_idx = 0;
|
||||
|
||||
if ( data == NULL ) return 0;
|
||||
|
@ -108,7 +107,10 @@ static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats )
|
|||
}
|
||||
}
|
||||
}
|
||||
if (sid_idx) save |= epggrab_channel_set_sid(ch, sid, sid_idx);
|
||||
if (sid_idx) {
|
||||
sid[sid_idx] = 0;
|
||||
save |= epggrab_channel_set_sid(ch, sid);
|
||||
}
|
||||
if (sname_idx) {
|
||||
sname[sname_idx] = NULL;
|
||||
save |= epggrab_channel_set_sname(ch, sname);
|
||||
|
@ -412,14 +414,8 @@ static int _pyepg_parse_epg ( htsmsg_t *data, epggrab_stats_t *stats )
|
|||
return save;
|
||||
}
|
||||
|
||||
|
||||
/* ************************************************************************
|
||||
* Module Setup
|
||||
* ***********************************************************************/
|
||||
|
||||
|
||||
static int _pyepg_parse
|
||||
( epggrab_module_t *mod, htsmsg_t *data, epggrab_stats_t *stats )
|
||||
( void *mod, htsmsg_t *data, epggrab_stats_t *stats )
|
||||
{
|
||||
htsmsg_t *tags, *epg;
|
||||
|
||||
|
@ -432,40 +428,23 @@ static int _pyepg_parse
|
|||
return 0;
|
||||
}
|
||||
|
||||
void pyepg_init ( epggrab_module_list_t *list )
|
||||
{
|
||||
epggrab_module_t *mod;
|
||||
/* ************************************************************************
|
||||
* Module Setup
|
||||
* ***********************************************************************/
|
||||
|
||||
/* Standard module */
|
||||
mod = calloc(1, sizeof(epggrab_module_t));
|
||||
mod->id = strdup("pyepg");
|
||||
mod->path = strdup("/usr/bin/pyepg");
|
||||
mod->name = strdup("PyEPG");
|
||||
mod->grab = epggrab_module_grab;
|
||||
mod->trans = epggrab_module_trans_xml;
|
||||
mod->parse = _pyepg_parse;
|
||||
mod->channels = &_pyepg_channels;
|
||||
mod->ch_add = epggrab_module_channel_add;
|
||||
mod->ch_rem = epggrab_module_channel_rem;
|
||||
mod->ch_mod = epggrab_module_channel_mod;
|
||||
*((uint8_t*)&mod->flags) = EPGGRAB_MODULE_INTERNAL;
|
||||
LIST_INSERT_HEAD(list, mod, link);
|
||||
_pyepg_module = mod;
|
||||
void pyepg_init ( void )
|
||||
{
|
||||
/* Internal module */
|
||||
epggrab_module_int_create(NULL, "/usr/bin/pyepg", "PyEPG", "/usr/bin/pyepg",
|
||||
NULL, _pyepg_parse, NULL, NULL);
|
||||
|
||||
/* External module */
|
||||
mod = calloc(1, sizeof(epggrab_module_t));
|
||||
mod->id = strdup("pyepg_ext");
|
||||
mod->path = epggrab_module_socket_path("pyepg");
|
||||
mod->name = strdup("PyEPG");
|
||||
mod->channels = &_pyepg_channels;
|
||||
mod->enable = epggrab_module_enable_socket;
|
||||
mod->trans = epggrab_module_trans_xml;
|
||||
mod->parse = _pyepg_parse;
|
||||
*((uint8_t*)&mod->flags) = EPGGRAB_MODULE_EXTERNAL;
|
||||
LIST_INSERT_HEAD(list, mod, link);
|
||||
epggrab_module_ext_create(NULL, "pyepg", "PyEPG", "pyepg",
|
||||
_pyepg_parse, NULL,
|
||||
&_pyepg_channels);
|
||||
}
|
||||
|
||||
void pyepg_load ( void )
|
||||
{
|
||||
epggrab_module_channels_load(_pyepg_module);
|
||||
epggrab_module_channels_load(epggrab_module_find_by_id("pyepg"));
|
||||
}
|
|
@ -31,22 +31,22 @@
|
|||
|
||||
#include "tvheadend.h"
|
||||
#include "channels.h"
|
||||
#include "epg.h"
|
||||
#include "xmltv.h"
|
||||
#include "spawn.h"
|
||||
|
||||
#include "epg.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/private.h"
|
||||
|
||||
#define XMLTV_FIND_GRABBERS "/usr/bin/tv_find_grabbers"
|
||||
|
||||
static epggrab_channel_tree_t _xmltv_channels;
|
||||
static epggrab_module_t *_xmltv_module;
|
||||
|
||||
static epggrab_channel_t *_xmltv_channel_find
|
||||
( const char *id, int create, int *save )
|
||||
{
|
||||
return epggrab_module_channel_find(_xmltv_module, id, create, save);
|
||||
return epggrab_channel_find(&_xmltv_channels, id, create, save);
|
||||
}
|
||||
|
||||
|
||||
/* **************************************************************************
|
||||
* Parsing
|
||||
* *************************************************************************/
|
||||
|
@ -442,12 +442,8 @@ _xmltv_parse_tv(htsmsg_t *body, epggrab_stats_t *stats)
|
|||
return save;
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
* Module Setup
|
||||
* ***********************************************************************/
|
||||
|
||||
static int _xmltv_parse
|
||||
( epggrab_module_t *mod, htsmsg_t *data, epggrab_stats_t *stats )
|
||||
( void *mod, htsmsg_t *data, epggrab_stats_t *stats )
|
||||
{
|
||||
htsmsg_t *tags, *tv;
|
||||
|
||||
|
@ -460,17 +456,20 @@ static int _xmltv_parse
|
|||
return _xmltv_parse_tv(tv, stats);
|
||||
}
|
||||
|
||||
static void _xmltv_load_grabbers ( epggrab_module_list_t *list )
|
||||
/* ************************************************************************
|
||||
* Module Setup
|
||||
* ***********************************************************************/
|
||||
|
||||
static void _xmltv_load_grabbers ( void )
|
||||
{
|
||||
size_t i, outlen, p, n;
|
||||
char *outbuf;
|
||||
char errbuf[100];
|
||||
epggrab_module_t *mod;
|
||||
char name[1000];
|
||||
|
||||
/* Load data */
|
||||
outlen = spawn_and_store_stdout(XMLTV_FIND_GRABBERS, NULL, &outbuf);
|
||||
if ( outlen < 1 ) {
|
||||
tvhlog(LOG_ERR, "xmltv", "%s failed [%s]", XMLTV_FIND_GRABBERS, errbuf);
|
||||
tvhlog(LOG_ERR, "xmltv", "%s failed", XMLTV_FIND_GRABBERS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -479,16 +478,9 @@ static void _xmltv_load_grabbers ( epggrab_module_list_t *list )
|
|||
while ( i < outlen ) {
|
||||
if ( outbuf[i] == '\n' || outbuf[i] == '\0' ) {
|
||||
outbuf[i] = '\0';
|
||||
mod = calloc(1, sizeof(epggrab_module_t));
|
||||
mod->id = mod->path = strdup(&outbuf[p]);
|
||||
mod->name = malloc(200);
|
||||
mod->channels = &_xmltv_channels;
|
||||
sprintf((char*)mod->name, "XMLTV: %s", &outbuf[n]);
|
||||
*((uint8_t*)&mod->flags) = EPGGRAB_MODULE_INTERNAL;
|
||||
mod->grab = epggrab_module_grab;
|
||||
mod->trans = epggrab_module_trans_xml;
|
||||
mod->parse = _xmltv_parse;
|
||||
LIST_INSERT_HEAD(list, mod, link);
|
||||
sprintf(name, "XMLTV: %s", &outbuf[n]);
|
||||
epggrab_module_int_create(NULL, &outbuf[p], name, &outbuf[p],
|
||||
NULL, _xmltv_parse, NULL, NULL);
|
||||
p = n = i + 1;
|
||||
} else if ( outbuf[i] == '|' ) {
|
||||
outbuf[i] = '\0';
|
||||
|
@ -499,31 +491,18 @@ static void _xmltv_load_grabbers ( epggrab_module_list_t *list )
|
|||
free(outbuf);
|
||||
}
|
||||
|
||||
void xmltv_init ( epggrab_module_list_t *list )
|
||||
void xmltv_init ( void )
|
||||
{
|
||||
epggrab_module_t *mod;
|
||||
|
||||
/* External module */
|
||||
mod = calloc(1, sizeof(epggrab_module_t));
|
||||
mod->id = strdup("xmltv");
|
||||
mod->name = strdup("XMLTV");
|
||||
mod->path = epggrab_module_socket_path("xmltv");
|
||||
mod->enable = epggrab_module_enable_socket;
|
||||
mod->trans = epggrab_module_trans_xml;
|
||||
mod->parse = _xmltv_parse;
|
||||
mod->channels = &_xmltv_channels;
|
||||
mod->ch_add = epggrab_module_channel_add;
|
||||
mod->ch_rem = epggrab_module_channel_rem;
|
||||
mod->ch_mod = epggrab_module_channel_mod;
|
||||
*((uint8_t*)&mod->flags) = EPGGRAB_MODULE_EXTERNAL;
|
||||
LIST_INSERT_HEAD(list, mod, link);
|
||||
_xmltv_module = mod;
|
||||
epggrab_module_ext_create(NULL, "xmltv", "XMLTV", "xmltv",
|
||||
_xmltv_parse, NULL,
|
||||
&_xmltv_channels);
|
||||
|
||||
/* Standard modules */
|
||||
_xmltv_load_grabbers(list);
|
||||
_xmltv_load_grabbers();
|
||||
}
|
||||
|
||||
void xmltv_load ( void )
|
||||
{
|
||||
epggrab_module_channels_load(_xmltv_module);
|
||||
epggrab_module_channels_load(epggrab_module_find_by_id("xmltv"));
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Electronic Program Guide - opentv
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __TVH_EPGGRAB_OPENTV_H__
|
||||
#define __TVH_EPGGRAB_OPENTV_H__
|
||||
|
||||
#include "dvb/dvb.h"
|
||||
#include "epggrab.h"
|
||||
|
||||
void opentv_tune ( th_dvb_mux_instance_t *tdmi );
|
||||
void opentv_init ( epggrab_module_list_t *list );
|
||||
void opentv_load ( void );
|
||||
|
||||
#endif /* __TVH_EPGGRAB_OPENTV_H__ */
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* Electronic Program Guide - EPG grabber OTA 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 "tvheadend.h"
|
||||
#include "queue.h"
|
||||
#include "epg.h"
|
||||
#include "dvb/dvb.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/ota.h"
|
||||
|
||||
LIST_HEAD(,epggrab_ota_mux) ota_muxes;
|
||||
|
||||
epggrab_ota_mux_t *epggrab_ota_register
|
||||
( epggrab_module_t *mod, th_dvb_mux_instance_t *tdmi,
|
||||
int timeout, int interval )
|
||||
{
|
||||
epggrab_ota_mux_t *ota;
|
||||
|
||||
/* Check for existing */
|
||||
LIST_FOREACH(ota, &ota_muxes, glob_link) {
|
||||
if (ota->grab == mod && ota->tdmi == tdmi) {
|
||||
ota->timeout = MAX(ota->timeout, timeout);
|
||||
ota->interval = MIN(ota->interval, interval);
|
||||
return ota;
|
||||
}
|
||||
}
|
||||
|
||||
/* Install new */
|
||||
ota = calloc(1, sizeof(epggrab_ota_mux_t));
|
||||
ota->grab = mod;
|
||||
ota->tdmi = tdmi;
|
||||
ota->timeout = timeout;
|
||||
ota->interval = interval;
|
||||
LIST_INSERT_HEAD(&ota_muxes, ota, glob_link);
|
||||
LIST_INSERT_HEAD(&tdmi->tdmi_epg_grabbers, ota, tdmi_link);
|
||||
LIST_INSERT_HEAD(&mod->muxes, ota, grab_link);
|
||||
|
||||
/* Re-assign to correct queue */
|
||||
if (tdmi->tdmi_scan_queue)
|
||||
TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
|
||||
dvb_mux_add_to_scan_queue(tdmi);
|
||||
|
||||
return ota;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister
|
||||
*/
|
||||
void epggrab_ota_unregister ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
LIST_REMOVE(ota, glob_link);
|
||||
LIST_REMOVE(ota, tdmi_link);
|
||||
LIST_REMOVE(ota, grab_link);
|
||||
free(ota);
|
||||
}
|
||||
|
||||
void epggrab_ota_unregister_one
|
||||
( epggrab_module_t *mod, th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
epggrab_ota_mux_t *ota;
|
||||
LIST_FOREACH(ota, &ota_muxes, glob_link) {
|
||||
if (ota->grab == mod && (!tdmi || ota->tdmi == tdmi)) {
|
||||
epggrab_ota_unregister(ota);
|
||||
if (tdmi) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_ota_unregister_all
|
||||
( epggrab_module_t *mod )
|
||||
{
|
||||
epggrab_ota_unregister_one(mod, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
int epggrab_ota_begin ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
if (ota->status == EPGGRAB_OTA_IDLE) {
|
||||
ota->status = EPGGRAB_OTA_STARTED;
|
||||
time(&ota->started);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void epggrab_ota_complete ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
if (ota->status != EPGGRAB_OTA_COMPLETED)
|
||||
time(&ota->completed);
|
||||
ota->status = EPGGRAB_OTA_COMPLETED;
|
||||
}
|
||||
|
||||
void epggrab_ota_cancel ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
ota->status = EPGGRAB_OTA_IDLE;
|
||||
}
|
||||
|
||||
void epggrab_ota_timeout ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
epggrab_ota_complete(ota);
|
||||
}
|
||||
|
||||
int epggrab_ota_is_complete ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
return ota->status == EPGGRAB_OTA_COMPLETED;
|
||||
}
|
||||
|
||||
int epggrab_ota_is_blocked ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
time_t t;
|
||||
time(&t);
|
||||
return t < (ota->completed + ota->interval);
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Electronic Program Guide - EPG grabber OTA 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __TVH_EPGGRAB_OTA_H__
|
||||
#define __TVH_EPGGRAB_OTA_H__
|
||||
|
||||
#include "queue.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
/*
|
||||
* Mux to OTA Grabber link
|
||||
*/
|
||||
typedef struct epggrab_ota_mux
|
||||
{
|
||||
LIST_ENTRY(epggrab_ota_mux) glob_link; ///< Global list of all instances
|
||||
LIST_ENTRY(epggrab_ota_mux) grab_link; ///< Grabber's list link
|
||||
LIST_ENTRY(epggrab_ota_mux) tdmi_link; ///< Mux list link
|
||||
struct epggrab_module *grab; ///< Grabber module
|
||||
struct th_dvb_mux_instance *tdmi; ///< Mux instance
|
||||
|
||||
int timeout; ///< Time out if this long
|
||||
int interval; ///< Re-grab this often
|
||||
|
||||
enum {
|
||||
EPGGRAB_OTA_IDLE,
|
||||
EPGGRAB_OTA_STARTED,
|
||||
EPGGRAB_OTA_COMPLETED
|
||||
} status; ///< Status of the scan
|
||||
time_t started; ///< Time of last start
|
||||
time_t completed; ///< Time of last completion
|
||||
|
||||
} epggrab_ota_mux_t;
|
||||
|
||||
/*
|
||||
* Register interest (will return existing if already registered)
|
||||
*/
|
||||
epggrab_ota_mux_t *epggrab_ota_register
|
||||
( struct epggrab_module *mod, struct th_dvb_mux_instance *tdmi,
|
||||
int timeout, int interval );
|
||||
|
||||
/*
|
||||
* Unregister
|
||||
*/
|
||||
void epggrab_ota_unregister
|
||||
( epggrab_ota_mux_t *ota );
|
||||
void epggrab_ota_unregister_one
|
||||
( struct epggrab_module *mod, struct th_dvb_mux_instance *tdmi );
|
||||
void epggrab_ota_unregister_all
|
||||
( struct epggrab_module *mod );
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
int epggrab_ota_begin ( epggrab_ota_mux_t *ota );
|
||||
void epggrab_ota_complete ( epggrab_ota_mux_t *ota );
|
||||
void epggrab_ota_cancel ( epggrab_ota_mux_t *ota );
|
||||
void epggrab_ota_timeout ( epggrab_ota_mux_t *ota );
|
||||
int epggrab_ota_is_complete ( epggrab_ota_mux_t *ota );
|
||||
int epggrab_ota_is_blocked ( epggrab_ota_mux_t *ota );
|
||||
|
||||
#endif /* __TVH_EPGGRAB_OTA_H__ */
|
311
src/epggrab/otamux.c
Normal file
311
src/epggrab/otamux.c
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Electronic Program Guide - EPG grabber OTA 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: currently I don't try to block multiple scans of the same
|
||||
* tdmi on different adapters
|
||||
*/
|
||||
|
||||
#include "tvheadend.h"
|
||||
#include "queue.h"
|
||||
#include "epg.h"
|
||||
#include "dvb/dvb.h"
|
||||
#include "epggrab.h"
|
||||
#include "epggrab/private.h"
|
||||
|
||||
TAILQ_HEAD(, epggrab_ota_mux) ota_mux_all;
|
||||
|
||||
/* **************************************************************************
|
||||
* Global functions (called from DVB code)
|
||||
* *************************************************************************/
|
||||
|
||||
void epggrab_mux_start ( th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
epggrab_module_t *m;
|
||||
LIST_FOREACH(m, &epggrab_modules, link) {
|
||||
if (m->type == EPGGRAB_OTA) {
|
||||
((epggrab_module_ota_t*)m)->start((epggrab_module_ota_t*)m, tdmi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_mux_stop ( th_dvb_mux_instance_t *tdmi, int timeout )
|
||||
{
|
||||
// Note: the slightly akward list iteration here is because
|
||||
// _ota_cancel/delete can remove the object and free() it
|
||||
epggrab_ota_mux_t *a, *b;
|
||||
a = TAILQ_FIRST(&tdmi->tdmi_epg_grab);
|
||||
while (a) {
|
||||
b = TAILQ_NEXT(a, tdmi_link);
|
||||
if (a->tdmi == tdmi) {
|
||||
if (timeout)
|
||||
epggrab_ota_timeout(a);
|
||||
else
|
||||
epggrab_ota_cancel(a);
|
||||
}
|
||||
a = b;
|
||||
}
|
||||
}
|
||||
|
||||
void epggrab_mux_delete ( th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
epggrab_ota_destroy_by_tdmi(tdmi);
|
||||
}
|
||||
|
||||
int epggrab_mux_period ( th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
int period = 0;
|
||||
epggrab_ota_mux_t *ota;
|
||||
TAILQ_FOREACH(ota, &tdmi->tdmi_epg_grab, tdmi_link) {
|
||||
if (!ota->is_reg) continue;
|
||||
if (ota->timeout > period)
|
||||
period = ota->timeout;
|
||||
}
|
||||
return period;
|
||||
}
|
||||
|
||||
th_dvb_mux_instance_t *epggrab_mux_next ( th_dvb_adapter_t *tda )
|
||||
{
|
||||
time_t now;
|
||||
epggrab_ota_mux_t *ota;
|
||||
time(&now);
|
||||
TAILQ_FOREACH(ota, &ota_mux_all, glob_link) {
|
||||
if (ota->interval + ota->completed > now) return NULL;
|
||||
if (!ota->is_reg) return NULL;
|
||||
if (ota->tdmi->tdmi_adapter == tda) break;
|
||||
}
|
||||
return ota ? ota->tdmi : NULL;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* OTA Mux link functions
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* Comprison of ota_mux instances based on when they can next run
|
||||
*
|
||||
* Note: ordering isn't garaunteed to be perfect as we use the current time
|
||||
*/
|
||||
static int _ota_time_cmp ( void *_a, void *_b )
|
||||
{
|
||||
int r;
|
||||
time_t now, wa, wb;
|
||||
time(&now);
|
||||
epggrab_ota_mux_t *a = _a;
|
||||
epggrab_ota_mux_t *b = _b;
|
||||
|
||||
/* Unreg'd always at the end */
|
||||
r = a->is_reg - b->is_reg;
|
||||
if (r) return r;
|
||||
|
||||
/* Check when */
|
||||
wa = a->completed + a->interval;
|
||||
wb = b->completed + b->interval;
|
||||
if (wa < now && wb < now)
|
||||
return a->started - b->started;
|
||||
else
|
||||
return wa - wb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create (temporary) or Find (existing) link
|
||||
*/
|
||||
epggrab_ota_mux_t *epggrab_ota_create
|
||||
( epggrab_module_ota_t *mod, th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
/* Search for existing */
|
||||
epggrab_ota_mux_t *ota;
|
||||
TAILQ_FOREACH(ota, &ota_mux_all, glob_link) {
|
||||
if (ota->grab == mod && ota->tdmi == tdmi) break;
|
||||
}
|
||||
|
||||
/* Create new */
|
||||
if (!ota) {
|
||||
ota = calloc(1, sizeof(epggrab_ota_mux_t));
|
||||
ota->grab = mod;
|
||||
ota->tdmi = tdmi;
|
||||
TAILQ_INSERT_TAIL(&ota_mux_all, ota, glob_link);
|
||||
TAILQ_INSERT_TAIL(&tdmi->tdmi_epg_grab, ota, tdmi_link);
|
||||
TAILQ_INSERT_TAIL(&mod->muxes, ota, grab_link);
|
||||
|
||||
} else {
|
||||
time_t now;
|
||||
time(&now);
|
||||
ota->state = EPGGRAB_OTA_MUX_IDLE;
|
||||
|
||||
/* Blocked */
|
||||
if (epggrab_ota_is_blocked(ota)) ota = NULL;
|
||||
}
|
||||
return ota;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create and register using mux ID
|
||||
*/
|
||||
void epggrab_ota_create_and_register_by_id
|
||||
( epggrab_module_ota_t *mod, int nid, int tsid, int period, int interval )
|
||||
{
|
||||
th_dvb_adapter_t *tda;
|
||||
th_dvb_mux_instance_t *tdmi;
|
||||
epggrab_ota_mux_t *ota;
|
||||
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
|
||||
//TODO: if (tda->nitoid != nid) continue;
|
||||
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
|
||||
if (tdmi->tdmi_transport_stream_id != tsid) continue;
|
||||
ota = epggrab_ota_create(mod, tdmi);
|
||||
epggrab_ota_register(ota, period, interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Destrory link (either because it was temporary OR mux deleted)
|
||||
*/
|
||||
void epggrab_ota_destroy ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
TAILQ_REMOVE(&ota_mux_all, ota, glob_link);
|
||||
TAILQ_REMOVE(&ota->tdmi->tdmi_epg_grab, ota, tdmi_link);
|
||||
TAILQ_REMOVE(&ota->grab->muxes, ota, grab_link);
|
||||
if (ota->destroy) ota->destroy(ota);
|
||||
else {
|
||||
if (ota->status) free(ota->status);
|
||||
free(ota);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy by tdmi
|
||||
*/
|
||||
void epggrab_ota_destroy_by_tdmi ( th_dvb_mux_instance_t *tdmi )
|
||||
{
|
||||
epggrab_ota_mux_t *ota;
|
||||
while ((ota = TAILQ_FIRST(&tdmi->tdmi_epg_grab)))
|
||||
epggrab_ota_destroy(ota);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy by module
|
||||
*/
|
||||
void epggrab_ota_destroy_by_module ( epggrab_module_ota_t *mod )
|
||||
{
|
||||
epggrab_ota_mux_t *ota;
|
||||
while ((ota = TAILQ_FIRST(&mod->muxes)))
|
||||
epggrab_ota_destroy(ota);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register interest (called when useful data exists on the MUX
|
||||
* thus inserting it into the EPG scanning queue
|
||||
*/
|
||||
void epggrab_ota_register ( epggrab_ota_mux_t *ota, int timeout, int interval )
|
||||
{
|
||||
int up = 0;
|
||||
ota->is_reg = 1;
|
||||
if (timeout > ota->timeout) {
|
||||
up = 1;
|
||||
ota->timeout = timeout;
|
||||
}
|
||||
if (interval > ota->interval) {
|
||||
up = 1;
|
||||
ota->interval = interval;
|
||||
}
|
||||
|
||||
if (up) {
|
||||
TAILQ_REMOVE(&ota_mux_all, ota, glob_link);
|
||||
TAILQ_INSERT_SORTED(&ota_mux_all, ota, glob_link, _ota_time_cmp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* State changes
|
||||
*/
|
||||
|
||||
static void _epggrab_ota_finished ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
/* Temporary link - delete it */
|
||||
if (!ota->is_reg)
|
||||
epggrab_ota_destroy(ota);
|
||||
|
||||
/* Reinsert into reg queue */
|
||||
else {
|
||||
TAILQ_REMOVE(&ota_mux_all, ota, glob_link);
|
||||
TAILQ_INSERT_SORTED(&ota_mux_all, ota, glob_link, _ota_time_cmp);
|
||||
}
|
||||
}
|
||||
|
||||
int epggrab_ota_begin ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
if (ota->state == EPGGRAB_OTA_MUX_IDLE) {
|
||||
ota->state = EPGGRAB_OTA_MUX_RUNNING;
|
||||
time(&ota->started);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void epggrab_ota_complete ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
th_dvb_mux_instance_t *tdmi = ota->tdmi;
|
||||
|
||||
if (ota->state != EPGGRAB_OTA_MUX_COMPLETE) {
|
||||
ota->state = EPGGRAB_OTA_MUX_COMPLETE;
|
||||
time(&ota->completed);
|
||||
|
||||
/* Check others */
|
||||
TAILQ_FOREACH(ota, &tdmi->tdmi_epg_grab, tdmi_link) {
|
||||
if (ota->is_reg && ota->state == EPGGRAB_OTA_MUX_RUNNING) break;
|
||||
}
|
||||
|
||||
/* All complete (bring timer forward) */
|
||||
if (!ota) {
|
||||
gtimer_arm(&tdmi->tdmi_adapter->tda_mux_scanner_timer,
|
||||
dvb_adapter_mux_scanner, tdmi->tdmi_adapter, 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset */
|
||||
void epggrab_ota_cancel ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
ota->state = EPGGRAB_OTA_MUX_IDLE;
|
||||
_epggrab_ota_finished(ota);
|
||||
}
|
||||
|
||||
/* Same as complete */
|
||||
void epggrab_ota_timeout ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
epggrab_ota_complete(ota);
|
||||
_epggrab_ota_finished(ota);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check status
|
||||
*/
|
||||
|
||||
int epggrab_ota_is_complete ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
return ota->state == EPGGRAB_OTA_MUX_COMPLETE ||
|
||||
ota->state == EPGGRAB_OTA_MUX_TIMEDOUT;
|
||||
}
|
||||
|
||||
int epggrab_ota_is_blocked ( epggrab_ota_mux_t *ota )
|
||||
{
|
||||
time_t t;
|
||||
time(&t);
|
||||
return t < (ota->completed + ota->interval);
|
||||
}
|
154
src/epggrab/private.h
Normal file
154
src/epggrab/private.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* EPG Grabber - private routines
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __EPGGRAB_PRIVATE_H__
|
||||
#define __EPGGRAB_PRIVATE_H__
|
||||
|
||||
/* **************************************************************************
|
||||
* Generic module routines
|
||||
* *************************************************************************/
|
||||
|
||||
epggrab_module_t *epggrab_module_create
|
||||
( epggrab_module_t *skel,
|
||||
const char *id, const char *name, epggrab_channel_tree_t *channels );
|
||||
|
||||
char *epggrab_module_grab_spawn ( void *m );
|
||||
htsmsg_t *epggrab_module_trans_xml ( void *m, char *data );
|
||||
|
||||
void epggrab_module_ch_add ( void *m, struct channel *ch );
|
||||
void epggrab_module_ch_rem ( void *m, struct channel *ch );
|
||||
void epggrab_module_ch_mod ( void *m, struct channel *ch );
|
||||
void epggrab_module_ch_save ( void *m, epggrab_channel_t *ec );
|
||||
|
||||
int epggrab_module_enable_socket ( void *m, uint8_t e );
|
||||
|
||||
void epggrab_module_parse ( void *m, htsmsg_t *data );
|
||||
|
||||
void epggrab_module_channels_load ( epggrab_module_t *m );
|
||||
|
||||
/* **************************************************************************
|
||||
* Channel processing
|
||||
* *************************************************************************/
|
||||
|
||||
int epggrab_channel_link ( epggrab_channel_t *ec, struct channel *ch );
|
||||
|
||||
epggrab_channel_t *epggrab_channel_find
|
||||
( epggrab_channel_tree_t *chs, const char *id, int create, int *save );
|
||||
|
||||
/* **************************************************************************
|
||||
* Internal module routines
|
||||
* *************************************************************************/
|
||||
|
||||
epggrab_module_int_t *epggrab_module_int_create
|
||||
( epggrab_module_int_t *skel,
|
||||
const char *id, const char *name, const char *path,
|
||||
char* (*grab) (void*m),
|
||||
int (*parse) (void *m, htsmsg_t *data, epggrab_stats_t *sta),
|
||||
htsmsg_t* (*trans) (void *mod, char *data),
|
||||
epggrab_channel_tree_t *channels );
|
||||
|
||||
/* **************************************************************************
|
||||
* External module routines
|
||||
* *************************************************************************/
|
||||
|
||||
epggrab_module_ext_t *epggrab_module_ext_create
|
||||
( epggrab_module_ext_t *skel,
|
||||
const char *id, const char *name, const char *sockid,
|
||||
int (*parse) (void *m, htsmsg_t *data, epggrab_stats_t *sta),
|
||||
htsmsg_t* (*trans) (void *mod, char *data),
|
||||
epggrab_channel_tree_t *channels );
|
||||
|
||||
/* **************************************************************************
|
||||
* OTA module routines
|
||||
* *************************************************************************/
|
||||
|
||||
epggrab_module_ota_t *epggrab_module_ota_create
|
||||
( epggrab_module_ota_t *skel,
|
||||
const char *id, const char *name,
|
||||
void (*start) (epggrab_module_ota_t*m,
|
||||
struct th_dvb_mux_instance *tdmi),
|
||||
int (*enable) (void *m, uint8_t e ),
|
||||
epggrab_channel_tree_t *channels );
|
||||
|
||||
/* **************************************************************************
|
||||
* OTA mux link routines
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* Create/Find a link (unregistered)
|
||||
*
|
||||
* Note: this will return NULL for an already existing link that is currently
|
||||
* blocked (i.e. has completed within interval period)
|
||||
*/
|
||||
epggrab_ota_mux_t *epggrab_ota_create
|
||||
( epggrab_module_ota_t *mod, struct th_dvb_mux_instance *tdmi );
|
||||
void epggrab_ota_create_and_register_by_id
|
||||
( epggrab_module_ota_t *mod, int nid, int tsid,
|
||||
int period, int interval );
|
||||
|
||||
/*
|
||||
* Delete
|
||||
*/
|
||||
void epggrab_ota_destroy ( epggrab_ota_mux_t *ota );
|
||||
void epggrab_ota_destroy_by_module ( epggrab_module_ota_t *mod );
|
||||
void epggrab_ota_destroy_by_tdmi ( struct th_dvb_mux_instance *tdmi );
|
||||
|
||||
/*
|
||||
* Register interest
|
||||
*/
|
||||
void epggrab_ota_register
|
||||
( epggrab_ota_mux_t *ota, int timeout, int interval );
|
||||
|
||||
/*
|
||||
* State change
|
||||
*/
|
||||
int epggrab_ota_begin ( epggrab_ota_mux_t *ota );
|
||||
void epggrab_ota_complete ( epggrab_ota_mux_t *ota );
|
||||
void epggrab_ota_cancel ( epggrab_ota_mux_t *ota );
|
||||
void epggrab_ota_timeout ( epggrab_ota_mux_t *ota );
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
int epggrab_ota_is_complete ( epggrab_ota_mux_t *ota );
|
||||
int epggrab_ota_is_blocked ( epggrab_ota_mux_t *ota );
|
||||
|
||||
/* **************************************************************************
|
||||
* Module setup(s)
|
||||
* *************************************************************************/
|
||||
|
||||
/* EIT module */
|
||||
void eit_init ( void );
|
||||
void eit_load ( void );
|
||||
|
||||
/* OpenTV module */
|
||||
void opentv_init ( void );
|
||||
void opentv_load ( void );
|
||||
|
||||
/* PyEPG module */
|
||||
void pyepg_init ( void );
|
||||
void pyepg_load ( void );
|
||||
|
||||
/* XMLTV module */
|
||||
void xmltv_init ( void );
|
||||
void xmltv_load ( void );
|
||||
|
||||
/* Note: this is reused by pyepg since they share a common format */
|
||||
int xmltv_parse_accessibility ( epg_broadcast_t *ebc, htsmsg_t *m );
|
||||
|
||||
#endif /* __EPGGRAB_PRIVATE_H__ */
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Electronic Program Guide - pyepg grabber
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef EPGGRAB_PYEPG_H
|
||||
#define EPGGRAB_PYEPG_H
|
||||
|
||||
#include "epggrab.h"
|
||||
|
||||
void pyepg_init ( epggrab_module_list_t *list );
|
||||
void pyepg_load ( void );
|
||||
|
||||
#endif
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Electronic Program Guide - xmltv grabber
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef EPGGRAB_XMLTV_H
|
||||
#define EPGGRAB_XMLTV_H
|
||||
|
||||
#include "htsmsg.h"
|
||||
#include "epggrab.h"
|
||||
|
||||
void xmltv_init ( epggrab_module_list_t *list );
|
||||
void xmltv_load ( void );
|
||||
|
||||
// reused by pyepg
|
||||
int xmltv_parse_accessibility ( epg_broadcast_t *ebc, htsmsg_t *m );
|
||||
|
||||
#endif
|
|
@ -7,16 +7,16 @@ tvheadend.epggrab = function() {
|
|||
/*
|
||||
* Module lists (I'm sure there is a better way!)
|
||||
*/
|
||||
var EPGGRAB_MODULE_INTERNAL = 0x01;
|
||||
var EPGGRAB_MODULE_EXTERNAL = 0x02;
|
||||
var EPGGRAB_MODULE_OTA = 0x04;
|
||||
var EPGGRAB_MODULE_INTERNAL = 0;
|
||||
var EPGGRAB_MODULE_EXTERNAL = 1;
|
||||
var EPGGRAB_MODULE_OTA = 2;
|
||||
|
||||
var moduleStore = new Ext.data.JsonStore({
|
||||
root : 'entries',
|
||||
url : 'epggrab',
|
||||
baseParams : { op : 'moduleList' },
|
||||
autoLoad : true,
|
||||
fields : [ 'id', 'name', 'path', 'flags', 'enabled' ]
|
||||
fields : [ 'id', 'name', 'path', 'type', 'enabled' ]
|
||||
});
|
||||
var internalModuleStore = new Ext.data.Store({
|
||||
recordType: moduleStore.recordType
|
||||
|
@ -29,7 +29,7 @@ tvheadend.epggrab = function() {
|
|||
});
|
||||
moduleStore.on('load', function() {
|
||||
moduleStore.filterBy(function(r) {
|
||||
return r.get('flags') & EPGGRAB_MODULE_INTERNAL;
|
||||
return r.get('type') == EPGGRAB_MODULE_INTERNAL;
|
||||
});
|
||||
r = new internalModuleStore.recordType({ id: '', name : 'Disabled'});
|
||||
internalModuleStore.add(r);
|
||||
|
@ -37,19 +37,19 @@ tvheadend.epggrab = function() {
|
|||
internalModuleStore.add(r.copy());
|
||||
});
|
||||
moduleStore.filterBy(function(r) {
|
||||
return r.get('flags') & EPGGRAB_MODULE_EXTERNAL;
|
||||
return r.get('type') == EPGGRAB_MODULE_EXTERNAL;
|
||||
});
|
||||
moduleStore.each(function(r) {
|
||||
externalModuleStore.add(r.copy());
|
||||
});
|
||||
moduleStore.filterBy(function(r) {
|
||||
return r.get('flags') & EPGGRAB_MODULE_OTA;
|
||||
return r.get('type') == EPGGRAB_MODULE_OTA;
|
||||
});
|
||||
moduleStore.each(function(r) {
|
||||
otaModuleStore.add(r.copy());
|
||||
});
|
||||
moduleStore.filterBy(function(r) {
|
||||
return r.get('flags') & (EPGGRAB_MODULE_OTA | EPGGRAB_MODULE_EXTERNAL);
|
||||
return r.get('type') != EPGGRAB_MODULE_INTERNAL;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue