diff --git a/Makefile b/Makefile index ea18e139..99c91a73 100644 --- a/Makefile +++ b/Makefile @@ -119,6 +119,7 @@ SRCS += \ src/api/api_service.c \ src/api/api_mpegts.c \ src/api/api_epg.c \ + src/api/api_epggrab.c \ SRCS += \ src/parsers/parsers.c \ diff --git a/src/api.c b/src/api.c index 408b9a2d..b4d4ed52 100644 --- a/src/api.c +++ b/src/api.c @@ -121,4 +121,5 @@ void api_init ( void ) api_service_init(); api_channel_init(); api_epg_init(); + api_epggrab_init(); } diff --git a/src/api.h b/src/api.h index b852cb8b..d2a2778d 100644 --- a/src/api.h +++ b/src/api.h @@ -62,6 +62,7 @@ void api_service_init ( void ); void api_channel_init ( void ); void api_mpegts_init ( void ); void api_epg_init ( void ); +void api_epggrab_init ( void ); /* * IDnode diff --git a/src/api/api_epggrab.c b/src/api/api_epggrab.c new file mode 100644 index 00000000..754f0551 --- /dev/null +++ b/src/api/api_epggrab.c @@ -0,0 +1,47 @@ +/* + * API - epggrab related calls + * + * Copyright (C) 2013 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 . + */ + +#include "tvheadend.h" +#include "access.h" +#include "api.h" +#include "epggrab.h" + +static int +api_epggrab_channel_list + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + htsmsg_t *m; + pthread_mutex_lock(&global_lock); + m = epggrab_channel_list(); + pthread_mutex_unlock(&global_lock); + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", m); + return 0; +} + +void api_epggrab_init ( void ) +{ + static api_hook_t ah[] = { + { "epggrab/channel/list", ACCESS_ANONYMOUS, + api_epggrab_channel_list, NULL }, + { NULL }, + }; + + api_register_all(ah); +} diff --git a/src/channels.c b/src/channels.c index e9b76cb2..7cebbb1b 100644 --- a/src/channels.c +++ b/src/channels.c @@ -205,6 +205,61 @@ channel_class_get_number ( void *p ) return &i; } +static const void * +channel_class_epggrab_get ( void *o ) +{ + channel_t *ch = o; + htsmsg_t *l = htsmsg_create_list(); + epggrab_channel_link_t *ecl; + LIST_FOREACH(ecl, &ch->ch_epggrab, ecl_chn_link) + htsmsg_add_str(l, NULL, epggrab_channel_get_id(ecl->ecl_epggrab)); + return l; +} + +static int +channel_class_epggrab_set ( void *o, const void *v ) +{ + int save = 0; + channel_t *ch = o; + htsmsg_t *l = (htsmsg_t*)v; + htsmsg_field_t *f; + epggrab_channel_t *ec; + epggrab_channel_link_t *ecl, *n; + + /* mark for deletion */ + LIST_FOREACH(ecl, &ch->ch_epggrab, ecl_chn_link) + ecl->ecl_mark = 1; + + /* Link */ + HTSMSG_FOREACH(f, l) { + if ((ec = epggrab_channel_find_by_id(htsmsg_field_get_str(f)))) + save |= epggrab_channel_link(ec, ch); + } + + /* Delete */ + for (ecl = LIST_FIRST(&ch->ch_epggrab); ecl != NULL; ecl = n) { + n = LIST_NEXT(ecl, ecl_chn_link); + if (ecl->ecl_mark) { + epggrab_channel_link_delete(ecl); + save = 1; + } + } + return save; +} + +static htsmsg_t * +channel_class_epggrab_list ( void *o ) +{ + htsmsg_t *e, *m = htsmsg_create_map(); + htsmsg_add_str(m, "type", "api"); + htsmsg_add_str(m, "uri", "epggrab/channel/list"); + htsmsg_add_str(m, "event", "epggrabchannel"); + e = htsmsg_create_map(); + htsmsg_add_bool(e, "enum", 1); + htsmsg_add_msg(m, "params", e); + return m; +} + const idclass_t channel_class = { .ic_class = "service", .ic_caption = "Service", @@ -241,6 +296,16 @@ const idclass_t channel_class = { .off = offsetof(channel_t, ch_icon), .notify = channel_class_icon_notify, }, + { + .type = PT_STR, + .islist = 1, + .id = "epggrab", + .name = "EPG Source", + .set = channel_class_epggrab_set, + .get = channel_class_epggrab_get, + .list = channel_class_epggrab_list, + .opts = PO_NOSAVE, + }, { .type = PT_INT, .id = "dvr_pre_time", @@ -427,6 +492,9 @@ channel_create0 ch->ch_name = strdup(name); } + /* EPG */ + epggrab_channel_add(ch); + return ch; } diff --git a/src/channels.h b/src/channels.h index f22053d9..080079aa 100644 --- a/src/channels.h +++ b/src/channels.h @@ -62,6 +62,8 @@ typedef struct channel gtimer_t ch_epg_timer_head; gtimer_t ch_epg_timer_current; + LIST_HEAD(,epggrab_channel_link) ch_epggrab; + /* DVR */ int ch_dvr_extra_time_pre; int ch_dvr_extra_time_post; @@ -152,6 +154,7 @@ int channel_set_name ( channel_t *ch, const char *s ); int channel_get_number ( channel_t *ch ); const char *channel_get_icon ( channel_t *ch ); +int channel_set_icon ( channel_t *ch, const char *icon ); #define channel_get_uuid(ch) idnode_uuid_as_str(&ch->ch_id) diff --git a/src/epggrab.h b/src/epggrab.h index 6c3358b5..3d400c9b 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -88,8 +88,11 @@ typedef struct epggrab_channel typedef struct epggrab_channel_link { - LIST_ENTRY(epggrab_channel_link) link; ///< Link to grab channel - struct channel *channel; ///< Real channel + int ecl_mark; + struct channel *ecl_channel; + struct epggrab_channel *ecl_epggrab; + LIST_ENTRY(epggrab_channel_link) ecl_chn_link; + LIST_ENTRY(epggrab_channel_link) ecl_epg_link; } epggrab_channel_link_t; /* @@ -107,7 +110,13 @@ int epggrab_channel_set_number ( epggrab_channel_t *ch, int number ); /* * Updated/link */ -void epggrab_channel_updated ( epggrab_channel_t *ch ); +void epggrab_channel_updated ( epggrab_channel_t *ch ); +void epggrab_channel_link_delete ( epggrab_channel_link_t *ecl ); +int epggrab_channel_link ( epggrab_channel_t *ec, struct channel *ch ); + +/* ID */ +const char *epggrab_channel_get_id ( epggrab_channel_t *ch ); +epggrab_channel_t *epggrab_channel_find_by_id ( const char *id ); /* ************************************************************************** * Grabber Modules diff --git a/src/epggrab/channel.c b/src/epggrab/channel.c index f856bba3..380836aa 100644 --- a/src/epggrab/channel.c +++ b/src/epggrab/channel.c @@ -23,7 +23,6 @@ #include "settings.h" #include "htsmsg.h" #include "channels.h" -#include "service.h" #include "epg.h" #include "epggrab.h" #include "epggrab/private.h" @@ -42,33 +41,55 @@ int epggrab_channel_match ( epggrab_channel_t *ec, channel_t *ch ) return 0; } +/* Destroy */ +void +epggrab_channel_link_delete + ( epggrab_channel_link_t *ecl ) +{ + LIST_REMOVE(ecl, ecl_chn_link); + LIST_REMOVE(ecl, ecl_epg_link); + if (ecl->ecl_epggrab->mod->ch_save) + ecl->ecl_epggrab->mod->ch_save(ecl->ecl_epggrab->mod, ecl->ecl_epggrab); + free(ecl); +} + /* Link epggrab channel to real channel */ -void epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch ) +int +epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch ) { epggrab_channel_link_t *ecl; /* No change */ - if (!ch) return; - LIST_FOREACH(ecl, &ec->channels, link) - if (ecl->channel == ch) return; + if (!ch) return 0; + /* Already linked */ + LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) { + if (ecl->ecl_channel == ch) { + ecl->ecl_mark = 0; + return 0; + } + } + + /* New link */ tvhlog(LOG_INFO, ec->mod->id, "linking %s to %s", ec->id, channel_get_name(ch)); ecl = calloc(1, sizeof(epggrab_channel_link_t)); - ecl->channel = ch; - LIST_INSERT_HEAD(&ec->channels, ecl, link); -#if 0 + ecl->ecl_channel = ch; + ecl->ecl_epggrab = ec; + LIST_INSERT_HEAD(&ec->channels, ecl, ecl_epg_link); + LIST_INSERT_HEAD(&ch->ch_epggrab, ecl, ecl_chn_link); +#if TODO_CHAN_UPDATE 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); + if (ec->icon && epggrab_channel_reicon) + channel_set_icon(ch, ec->icon); #endif /* Save */ if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec); - + return 1; } /* Match and link (basically combines two funcs above for ease) */ @@ -84,18 +105,19 @@ int epggrab_channel_match_and_link ( epggrab_channel_t *ec, channel_t *ch ) int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name ) { int save = 0; -#if 0 - epggrab_channel_link_t *ecl; if (!ec || !name) return 0; if (!ec->name || strcmp(ec->name, name)) { if (ec->name) free(ec->name); ec->name = strdup(name); - if (epggrab_channel_rename) +#if TODO_CHAN_UPDATE + if (epggrab_channel_rename) { + epggrab_channel_link_t *ecl; LIST_FOREACH(ecl, &ec->channels, link) channel_rename(ecl->channel, name); + } +#endif save = 1; } -#endif return save; } @@ -103,18 +125,19 @@ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name ) int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon ) { int save = 0; -#if 0 - epggrab_channel_link_t *ecl; if (!ec->icon || strcmp(ec->icon, icon) ) { if (!ec | !icon) return 0; if (ec->icon) free(ec->icon); ec->icon = strdup(icon); - if (epggrab_channel_reicon) +#if TODO_CHAN_UPDATE + if (epggrab_channel_reicon) { + epggrab_channel_link_t *ecl; LIST_FOREACH(ecl, &ec->channels, link) channel_set_icon(ecl->channel, icon); + } +#endif save = 1; } -#endif return save; } @@ -122,35 +145,34 @@ int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon ) int epggrab_channel_set_number ( epggrab_channel_t *ec, int number ) { int save = 0; -#if 0 - epggrab_channel_link_t *ecl; if (!ec || (number <= 0)) return 0; if (ec->number != number) { ec->number = number; - if (epggrab_channel_renumber) +#if TODO_CHAN_UPDATE + if (epggrab_channel_renumber) { + epggrab_channel_link_t *ecl; LIST_FOREACH(ecl, &ec->channels, link) channel_set_number(ecl->channel, number); + } +#endif save = 1; } -#endif return save; } /* Channel settings updated */ void epggrab_channel_updated ( epggrab_channel_t *ec ) { -#if 0 channel_t *ch; if (!ec) return; /* Find a link */ if (!LIST_FIRST(&ec->channels)) - RB_FOREACH(ch, &channel_name_tree, ch_name_link) + CHANNEL_FOREACH(ch) if (epggrab_channel_match_and_link(ec, ch)) break; /* Save */ if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec); -#endif } /* ID comparison */ @@ -167,7 +189,6 @@ epggrab_channel_t *epggrab_channel_find { epggrab_channel_t *ec; static epggrab_channel_t *skel = NULL; - assert(owner); if (!skel) skel = calloc(1, sizeof(epggrab_channel_t)); skel->id = (char*)id; @@ -179,6 +200,7 @@ epggrab_channel_t *epggrab_channel_find } else { ec = RB_INSERT_SORTED(tree, skel, link, _ch_id_cmp); if (!ec) { + assert(owner); ec = skel; skel = NULL; ec->id = strdup(id); @@ -204,15 +226,11 @@ htsmsg_t *epggrab_channel_list ( void ) if (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); - if (ec->name) - htsmsg_add_str(e, "name", ec->name); snprintf(name, sizeof(name), "%s|%s", mod->id, ec->id); - htsmsg_add_str(e, "mod-id", name); + htsmsg_add_str(e, "key", name); snprintf(name, sizeof(name), "%s: %s (%s)", mod->name, ec->name, ec->id); - htsmsg_add_str(e, "mod-name", name); + htsmsg_add_str(e, "val", name); htsmsg_add_msg(m, NULL, e); } } @@ -243,3 +261,25 @@ void epggrab_channel_mod ( channel_t *ch ) if (m->ch_mod) m->ch_mod(m, ch); } } + +const char * +epggrab_channel_get_id ( epggrab_channel_t *ec ) +{ + static char buf[1024]; + epggrab_module_t *m = ec->mod; + snprintf(buf, sizeof(buf), "%s|%s", m->id, ec->id); + return buf; +} + +epggrab_channel_t * +epggrab_channel_find_by_id ( const char *id ) +{ + char buf[1024]; + char *mid, *cid; + epggrab_module_t *mod; + strncpy(buf, id, sizeof(buf)); + if ((mid = strtok_r(buf, "|", &cid)) && cid) + if ((mod = epggrab_module_find_by_id(mid)) && mod->channels) + return epggrab_channel_find(mod->channels, cid, 0, NULL, NULL); + return NULL; +} diff --git a/src/epggrab/module.c b/src/epggrab/module.c index 8a7fdc74..a83be8dc 100644 --- a/src/epggrab/module.c +++ b/src/epggrab/module.c @@ -153,9 +153,9 @@ void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch ) htsmsg_add_str(m, "name", ch->name); if (ch->icon) htsmsg_add_str(m, "icon", ch->icon); - LIST_FOREACH(ecl, &ch->channels, link) { + LIST_FOREACH(ecl, &ch->channels, ecl_epg_link) { if (!a) a = htsmsg_create_list(); - htsmsg_add_u32(a, NULL, channel_get_id(ecl->channel)); + htsmsg_add_str(a, NULL, channel_get_uuid(ecl->ecl_channel)); } if (a) htsmsg_add_msg(m, "channels", a); if (ch->number) @@ -175,18 +175,9 @@ void epggrab_module_ch_add ( void *m, channel_t *ch ) void epggrab_module_ch_rem ( void *m, channel_t *ch ) { - epggrab_channel_t *egc; - epggrab_channel_link_t *egl; - epggrab_module_int_t *mod = m; - RB_FOREACH(egc, mod->channels, link) { - LIST_FOREACH(egl, &egc->channels, link) { - if (egl->channel == ch) { - LIST_REMOVE(egl, link); - free(egl); - break; - } - } - } + epggrab_channel_link_t *ecl; + while ((ecl = LIST_FIRST(&ch->ch_epggrab))) + epggrab_channel_link_delete(ecl); } void epggrab_module_ch_mod ( void *mod, channel_t *ch ) @@ -215,20 +206,14 @@ static void _epggrab_module_channel_load egc->number = u32; if ((a = htsmsg_get_list(m, "channels"))) { HTSMSG_FOREACH(f, a) { - if ((ch = channel_find_by_id((uint32_t)f->hmf_s64))) { - epggrab_channel_link_t *ecl = calloc(1, sizeof(epggrab_channel_link_t)); - ecl->channel = ch; - LIST_INSERT_HEAD(&egc->channels, ecl, link); - } + if ((ch = channel_find_by_uuid(f->hmf_str))) + epggrab_channel_link(egc, ch); } /* Compat with older 3.1 code */ } else if (!htsmsg_get_u32(m, "channel", &u32)) { - if ((ch = channel_find_by_id(u32))) { - epggrab_channel_link_t *ecl = calloc(1, sizeof(epggrab_channel_link_t)); - ecl->channel = ch; - LIST_INSERT_HEAD(&egc->channels, ecl, link); - } + if ((ch = channel_find_by_id(u32))) + epggrab_channel_link(egc, ch); } } diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index cc6584ba..3b2c7e02 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -290,11 +290,12 @@ opentv_parse_event_section /* Find broadcast */ if (ev.start && ev.stop) { - ebc = epg_broadcast_find_by_time(ecl->channel, ev.start, ev.stop, ev.eid, - 1, &save); - tvhdebug("opentv", "find by time start %ld stop %ld eid %d = %p", ev.start, ev.stop, ev.eid, ebc); + ebc = epg_broadcast_find_by_time(ecl->ecl_channel, ev.start, ev.stop, + ev.eid, 1, &save); + tvhdebug("opentv", "find by time start %ld stop %ld eid %d = %p", + ev.start, ev.stop, ev.eid, ebc); } else { - ebc = epg_broadcast_find_by_eid(ecl->channel, ev.eid); + ebc = epg_broadcast_find_by_eid(ecl->ecl_channel, ev.eid); tvhdebug("opentv", "find by eid %d = %p", ev.eid, ebc); } if (!ebc) @@ -317,7 +318,7 @@ opentv_parse_event_section if (ev.serieslink) { char suri[257]; snprintf(suri, 256, "opentv://channel-%s/series-%d", - channel_get_uuid(ecl->channel), ev.serieslink); + channel_get_uuid(ecl->ecl_channel), ev.serieslink); if ((es = epg_serieslink_find_by_uri(suri, 1, &save))) save |= epg_broadcast_set_serieslink(ebc, es, src); } @@ -385,6 +386,7 @@ opentv_desc_channels epggrab_channel_t *ec; epggrab_channel_link_t *ecl; mpegts_service_t *svc; + channel_t *ch; int sid, cid, cnum; int save = 0; int i = 2; @@ -400,12 +402,16 @@ opentv_desc_channels if (svc && LIST_FIRST(&svc->s_channels)) { ec =_opentv_find_epggrab_channel(mod, cid, 1, &save); ecl = LIST_FIRST(&ec->channels); + ch = LIST_FIRST(&svc->s_channels)->csm_chn; tvhtrace(mt->mt_name, " ec = %p, ecl = %p", ec, ecl); - if (!ecl) { - ecl = calloc(1, sizeof(epggrab_channel_link_t)); - LIST_INSERT_HEAD(&ec->channels, ecl, link); + + if (ecl && ecl->ecl_channel != ch) { + epggrab_channel_link_delete(ecl); + ecl = NULL; } - ecl->channel = LIST_FIRST(&svc->s_channels)->csm_chn; + + if (!ecl) + epggrab_channel_link(ec, ch); save |= epggrab_channel_set_number(ec, cnum); } i += 9; diff --git a/src/epggrab/module/pyepg.c b/src/epggrab/module/pyepg.c index abb3e264..d3b1e876 100644 --- a/src/epggrab/module/pyepg.c +++ b/src/epggrab/module/pyepg.c @@ -365,9 +365,9 @@ static int _pyepg_parse_schedule HTSMSG_FOREACH(f, tags) { if (strcmp(f->hmf_name, "broadcast") == 0) { - LIST_FOREACH(ecl, &ec->channels, link) + LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) save |= _pyepg_parse_broadcast(mod, htsmsg_get_map_by_field(f), - ecl->channel, stats); + ecl->ecl_channel, stats); } } diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c index 8c7bc389..ad6930d2 100644 --- a/src/epggrab/module/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -553,8 +553,8 @@ static int _xmltv_parse_programme if(stop <= start || stop <= dispatch_clock) return 0; - LIST_FOREACH(ecl, &ch->channels, link) - save |= _xmltv_parse_programme_tags(mod, ecl->channel, tags, + LIST_FOREACH(ecl, &ch->channels, ecl_epg_link) + save |= _xmltv_parse_programme_tags(mod, ecl->ecl_channel, tags, start, stop, stats); return save; } diff --git a/src/epggrab/private.h b/src/epggrab/private.h index 7f31e32b..50c31697 100644 --- a/src/epggrab/private.h +++ b/src/epggrab/private.h @@ -48,7 +48,6 @@ void epggrab_module_channels_load ( epggrab_module_t *m ); * Channel processing * *************************************************************************/ -void epggrab_channel_link ( epggrab_channel_t *ec, struct channel *ch ); int epggrab_channel_match ( epggrab_channel_t *ec, struct channel *ch ); int epggrab_channel_match_and_link ( epggrab_channel_t *ec, struct channel *ch );