From: Jaroslav Kysela Date: Thu, 22 Oct 2015 15:26:05 +0000 (+0200) Subject: epggrab: EPG channel mapping - move to idnode interface, fixes #3188 X-Git-Tag: v4.2.1~1840 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=628a1141e1749e8f35f442c3093a786dff0836b3;p=thirdparty%2Ftvheadend.git epggrab: EPG channel mapping - move to idnode interface, fixes #3188 --- diff --git a/src/api/api_epggrab.c b/src/api/api_epggrab.c index 83c341dd3..0ad1a25d0 100644 --- a/src/api/api_epggrab.c +++ b/src/api/api_epggrab.c @@ -22,17 +22,14 @@ #include "api.h" #include "epggrab.h" -static int -api_epggrab_channel_list - ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +static void +api_epggrab_channel_grid + ( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args ) { - htsmsg_t *m; - pthread_mutex_lock(&global_lock); - m = epggrab_channel_list(0); - pthread_mutex_unlock(&global_lock); - *resp = htsmsg_create_map(); - htsmsg_add_msg(*resp, "entries", m); - return 0; + epggrab_channel_t *ec; + + TAILQ_FOREACH(ec, &epggrab_channel_entries, all_link) + idnode_set_add(ins, (idnode_t*)ec, &conf->filter, perm->aa_lang_ui); } static int @@ -73,7 +70,10 @@ api_epggrab_ota_trigger void api_epggrab_init ( void ) { static api_hook_t ah[] = { - { "epggrab/channel/list", ACCESS_ANONYMOUS, api_epggrab_channel_list, NULL }, + { "epggrab/channel/list", ACCESS_ANONYMOUS, api_idnode_load_by_class, (void*)&epggrab_channel_class }, + { "epggrab/channel/class", ACCESS_ADMIN, api_idnode_class, (void*)&epggrab_channel_class }, + { "epggrab/channel/grid", ACCESS_ADMIN, api_idnode_grid, api_epggrab_channel_grid }, + { "epggrab/module/list", ACCESS_ADMIN, api_epggrab_module_list, NULL }, { "epggrab/config/load", ACCESS_ADMIN, api_idnode_load_simple, &epggrab_conf.idnode }, { "epggrab/config/save", ACCESS_ADMIN, api_idnode_save_simple, &epggrab_conf.idnode }, diff --git a/src/channels.c b/src/channels.c index 8906d189d..93e242ab5 100644 --- a/src/channels.c +++ b/src/channels.c @@ -233,48 +233,16 @@ 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) { - if (!epggrab_channel_is_ota(ecl->ecl_epggrab)) - htsmsg_add_str(l, NULL, epggrab_channel_get_id(ecl->ecl_epggrab)); - } - return l; + return idnode_list_get2(&ch->ch_epggrab); } 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) { - if (!epggrab_channel_is_ota(ecl->ecl_epggrab)) - ecl->ecl_mark = 1; - } - - /* Link */ - if (l) { - 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, 1); - save = 1; - } - } - return save; + return idnode_list_set2(&ch->ch_id, &ch->ch_epggrab, + &epggrab_channel_class, (htsmsg_t *)v, + epggrab_channel_map); } static htsmsg_t * @@ -283,7 +251,7 @@ channel_class_epggrab_list ( void *o, const char *lang ) 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"); + htsmsg_add_str(m, "event", "epggrab_channel"); e = htsmsg_create_map(); htsmsg_add_bool(e, "enum", 1); htsmsg_add_msg(m, "params", e); diff --git a/src/channels.h b/src/channels.h index e542b714a..22479a447 100644 --- a/src/channels.h +++ b/src/channels.h @@ -50,7 +50,7 @@ typedef struct channel /* Channel info */ int ch_enabled; int ch_autoname; - char *ch_name; // Note: do not access directly! + char *ch_name; /* Note: do not access directly! */ int64_t ch_number; char *ch_icon; idnode_list_head_t ch_ctms; @@ -69,7 +69,7 @@ typedef struct channel gtimer_t ch_epg_timer_current; int ch_epgauto; - LIST_HEAD(,epggrab_channel_link) ch_epggrab; + idnode_list_head_t ch_epggrab; /* 1 = epggrab channel, 2 = channel */ /* DVR */ int ch_dvr_extra_time_pre; diff --git a/src/config.c b/src/config.c index 5b7f558da..af7fe9b4d 100644 --- a/src/config.c +++ b/src/config.c @@ -32,6 +32,7 @@ #include "avahi.h" #include "url.h" #include "satip/server.h" +#include "channels.h" #include @@ -1346,6 +1347,39 @@ config_migrate_v22 ( void ) } } +/* + * v21 -> v23 : epggrab xmltv channels + */ +static void +config_migrate_v23 ( void ) +{ + htsmsg_t *c, *m, *n; + htsmsg_field_t *f; + uint32_t maj, min; + int64_t num; + tvh_uuid_t u; + + if ((c = hts_settings_load_r(1, "epggrab/xmltv/channels")) != NULL) { + HTSMSG_FOREACH(f, c) { + m = htsmsg_field_get_map(f); + n = htsmsg_copy(m); + htsmsg_add_str(n, "id", f->hmf_name); + maj = htsmsg_get_u32_or_default(m, "major", 0); + min = htsmsg_get_u32_or_default(m, "minor", 0); + num = (maj * CHANNEL_SPLIT) + min; + if (num > 0) + htsmsg_add_s64(n, "lcn", num); + htsmsg_delete_field(n, "major"); + htsmsg_delete_field(n, "minor"); + uuid_init_hex(&u, NULL); + hts_settings_remove("epggrab/xmltv/channels/%s", f->hmf_name); + hts_settings_save(n, "epggrab/xmltv/channels/%s", u.hex); + htsmsg_destroy(n); + } + htsmsg_destroy(c); + } +} + /* @@ -1466,7 +1500,8 @@ static const config_migrate_t config_migrate_table[] = { config_migrate_v19, config_migrate_v20, config_migrate_v21, - config_migrate_v22 + config_migrate_v22, + config_migrate_v23 }; /* diff --git a/src/epggrab.c b/src/epggrab.c index 799bf48e7..52f9687e1 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -355,6 +355,8 @@ void epggrab_init ( void ) pthread_mutex_init(&epggrab_mutex, NULL); pthread_cond_init(&epggrab_cond, NULL); + epggrab_channel_init(); + /* Initialise modules */ #if ENABLE_MPEGTS eit_init(); @@ -395,12 +397,12 @@ void epggrab_done ( void ) pthread_mutex_unlock(&global_lock); if (mod->done) mod->done(mod); + pthread_mutex_lock(&global_lock); + epggrab_channel_flush(mod->channels, 0); free((void *)mod->id); free((void *)mod->name); free(mod); - pthread_mutex_lock(&global_lock); } - pthread_mutex_unlock(&global_lock); epggrab_ota_shutdown(); eit_done(); opentv_done(); @@ -413,4 +415,5 @@ void epggrab_done ( void ) free(epggrab_conf.ota_cron); epggrab_conf.ota_cron = NULL; epggrab_channel_done(); + pthread_mutex_unlock(&global_lock); } diff --git a/src/epggrab.h b/src/epggrab.h index 1ab559f92..ab071bc99 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -39,6 +39,7 @@ LIST_HEAD(epggrab_module_list, epggrab_module); typedef struct epggrab_module_list epggrab_module_list_t; struct mpegts_mux; +struct channel; /* ************************************************************************** * Grabber Stats @@ -71,37 +72,34 @@ typedef struct epggrab_stats RB_HEAD(epggrab_channel_tree, epggrab_channel); typedef struct epggrab_channel_tree epggrab_channel_tree_t; +TAILQ_HEAD(epggrab_channel_queue, epggrab_channel); + /* * Grab channel */ typedef struct epggrab_channel { - RB_ENTRY(epggrab_channel) link; ///< Global link + idnode_t idnode; + TAILQ_ENTRY(epggrab_channel) all_link; ///< Global link + RB_ENTRY(epggrab_channel) link; ///< Global tree link + epggrab_channel_tree_t *tree; ///< Member of this tree epggrab_module_t *mod; ///< Linked module + int enabled; ///< Enabled/disabled char *id; ///< Grabber's ID char *name; ///< Channel name char *icon; ///< Channel icon - int major; ///< Channel major number - int minor; ///< Channel minor number + char *comment; ///< Channel comment (EPG) + int64_t lcn; ///< Channel number (split) - LIST_HEAD(,epggrab_channel_link) channels; ///< Mapped channels + idnode_list_head_t channels; ///< Mapped channels (1 = epggrab channel, 2 = channel) } epggrab_channel_t; -typedef struct epggrab_channel_link -{ - 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; - /* * Access functions */ -htsmsg_t* epggrab_channel_list ( int ota ); +htsmsg_t *epggrab_channel_list ( int ota ); /* * Mutators @@ -114,8 +112,9 @@ int epggrab_channel_set_number ( epggrab_channel_t *ch, int major, int minor ) * Updated/link */ void epggrab_channel_updated ( epggrab_channel_t *ch ); -void epggrab_channel_link_delete ( epggrab_channel_link_t *ecl, int delconf ); -int epggrab_channel_link ( epggrab_channel_t *ec, struct channel *ch ); +void epggrab_channel_link_delete ( epggrab_channel_t *ec, struct channel *ch, int delconf ); +int epggrab_channel_link ( epggrab_channel_t *ec, struct channel *ch, void *origin ); +int epggrab_channel_map ( idnode_t *ec, idnode_t *ch, void *origin ); /* ID */ const char *epggrab_channel_get_id ( epggrab_channel_t *ch ); @@ -155,12 +154,6 @@ struct epggrab_module /* Free */ void (*done) ( void *m ); - - /* Channel listings */ - 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 ); }; /* @@ -278,6 +271,8 @@ extern const idclass_t epggrab_class_mod; extern const idclass_t epggrab_class_mod_int; extern const idclass_t epggrab_class_mod_ext; extern const idclass_t epggrab_class_mod_ota; +extern const idclass_t epggrab_channel_class; +extern struct epggrab_channel_queue epggrab_channel_entries; /* * Access the Module list diff --git a/src/epggrab/channel.c b/src/epggrab/channel.c index 9d828898c..a01be7252 100644 --- a/src/epggrab/channel.c +++ b/src/epggrab/channel.c @@ -1,6 +1,7 @@ /* * EPG Grabber - channel functions * Copyright (C) 2012 Adam Sutton + * Copyright (C) 2015 Jaroslav Kysela * * 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 @@ -27,6 +28,8 @@ #include #include +struct epggrab_channel_queue epggrab_channel_entries; + SKEL_DECLARE(epggrab_channel_skel, epggrab_channel_t); /* ************************************************************************** @@ -36,89 +39,102 @@ SKEL_DECLARE(epggrab_channel_skel, epggrab_channel_t); /* Check if channels match */ int epggrab_channel_match ( epggrab_channel_t *ec, channel_t *ch ) { - if (!ec || !ch || !ch->ch_epgauto || !ch->ch_enabled) return 0; + if (!ec || !ch || !ch->ch_epgauto || !ch->ch_enabled || !ec->enabled) return 0; if (LIST_FIRST(&ec->channels)) return 0; // ignore already paired if (ec->name && !strcmp(ec->name, channel_get_epgid(ch))) return 1; - int64_t number = channel_get_number(ch); - if ((ec->major || ec->minor) && ec->major == channel_get_major(number) && ec->minor == channel_get_minor(number)) return 1; + if (ec->lcn && ec->lcn == channel_get_number(ch)) return 1; return 0; } /* Destroy */ void epggrab_channel_link_delete - ( epggrab_channel_link_t *ecl, int delconf ) + ( epggrab_channel_t *ec, channel_t *ch, int delconf ) +{ + idnode_list_mapping_t *ilm; + LIST_FOREACH(ilm, &ec->channels, ilm_in2_link) + if (ilm->ilm_in1 == &ec->idnode && ilm->ilm_in2 == &ch->ch_id) + idnode_list_unlink(ilm, NULL); +} + +/* Destroy all links */ +static void epggrab_channel_links_delete( epggrab_channel_t *ec, int delconf ) { - LIST_REMOVE(ecl, ecl_chn_link); - LIST_REMOVE(ecl, ecl_epg_link); - if (delconf && ecl->ecl_epggrab->mod->ch_save) - ecl->ecl_epggrab->mod->ch_save(ecl->ecl_epggrab->mod, ecl->ecl_epggrab); - free(ecl); + idnode_list_mapping_t *ilm; + while ((ilm = LIST_FIRST(&ec->channels))) + idnode_list_unlink(ilm, delconf ? ec : NULL); } /* Link epggrab channel to real channel */ int -epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch ) +epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch, void *origin ) { int save = 0; - epggrab_channel_link_t *ecl; + idnode_list_mapping_t *ilm; /* No change */ if (!ch || !ch->ch_enabled) return 0; /* Already linked */ - LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) { - if (ecl->ecl_channel == ch) { - ecl->ecl_mark = 0; + LIST_FOREACH(ilm, &ec->channels, ilm_in2_link) + if (ilm->ilm_in2 == &ch->ch_id) return 0; - } - } /* New link */ tvhdebug(ec->mod->id, "linking %s to %s", ec->id, channel_get_name(ch)); - ecl = calloc(1, sizeof(epggrab_channel_link_t)); - 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); + + ilm = idnode_list_link(&ec->idnode, &ec->channels, + &ch->ch_id, &ch->ch_epggrab, + origin, 1); + if (ilm == NULL) + return 0; + if (ec->name && epggrab_conf.channel_rename) save |= channel_set_name(ch, ec->name); - if ((ec->major > 0 || ec->minor > 0) && epggrab_conf.channel_renumber) - save |= channel_set_number(ch, ec->major, ec->minor); + if (ec->lcn > 0 && epggrab_conf.channel_renumber) + save |= channel_set_number(ch, ec->lcn / CHANNEL_SPLIT, ec->lcn % CHANNEL_SPLIT); if (ec->icon && epggrab_conf.channel_reicon) save |= channel_set_icon(ch, ec->icon); if (save) channel_save(ch); - /* Save */ - if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec); + if (origin == NULL) + epggrab_channel_save(ec); return 1; } +int +epggrab_channel_map ( idnode_t *ec, idnode_t *ch, void *origin ) +{ + return epggrab_channel_link((epggrab_channel_t *)ec, (channel_t *)ch, origin); +} + /* Match and link (basically combines two funcs above for ease) */ int epggrab_channel_match_and_link ( epggrab_channel_t *ec, channel_t *ch ) { int r = epggrab_channel_match(ec, ch); if (r) - epggrab_channel_link(ec, ch); + epggrab_channel_link(ec, ch, NULL); return r; } /* Set name */ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name ) { + idnode_list_mapping_t *ilm; + channel_t *ch; 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 (epggrab_conf.channel_rename) { - epggrab_channel_link_t *ecl; - LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) { - if (channel_set_name(ecl->ecl_channel, name)) - channel_save(ecl->ecl_channel); + LIST_FOREACH(ilm, &ec->channels, ilm_in2_link) { + ch = (channel_t *)ilm->ilm_in2; + if (channel_set_name(ch, name)) + channel_save(ch); } } save = 1; @@ -129,16 +145,18 @@ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name ) /* Set icon */ int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon ) { + idnode_list_mapping_t *ilm; + channel_t *ch; int save = 0; if (!ec || !icon) return 0; if (!ec->icon || strcmp(ec->icon, icon) ) { if (ec->icon) free(ec->icon); ec->icon = strdup(icon); if (epggrab_conf.channel_reicon) { - epggrab_channel_link_t *ecl; - LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) { - if (channel_set_icon(ecl->ecl_channel, icon)) - channel_save(ecl->ecl_channel); + LIST_FOREACH(ilm, &ec->channels, ilm_in2_link) { + ch = (channel_t *)ilm->ilm_in2; + if (channel_set_icon(ch, icon)) + channel_save(ch); } } save = 1; @@ -149,16 +167,21 @@ int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon ) /* Set channel number */ int epggrab_channel_set_number ( epggrab_channel_t *ec, int major, int minor ) { + idnode_list_mapping_t *ilm; + channel_t *ch; + int64_t lcn; int save = 0; if (!ec || (major <= 0 && minor <= 0)) return 0; - if (ec->major != major || ec->minor != minor) { - ec->major = major; - ec->minor = minor; + lcn = (major * CHANNEL_SPLIT) + minor; + if (ec->lcn != lcn) { + ec->lcn = lcn; if (epggrab_conf.channel_renumber) { - epggrab_channel_link_t *ecl; - LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) { - if (channel_set_number(ecl->ecl_channel, major, minor)) - channel_save(ecl->ecl_channel); + LIST_FOREACH(ilm, &ec->channels, ilm_in2_link) { + ch = (channel_t *)ilm->ilm_in2; + if (channel_set_number(ch, + lcn / CHANNEL_SPLIT, + lcn % CHANNEL_SPLIT)) + channel_save(ch); } } save = 1; @@ -178,7 +201,7 @@ void epggrab_channel_updated ( epggrab_channel_t *ec ) if (epggrab_channel_match_and_link(ec, ch)) break; /* Save */ - if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec); + epggrab_channel_save(ec); } /* ID comparison */ @@ -188,6 +211,36 @@ static int _ch_id_cmp ( void *a, void *b ) ((epggrab_channel_t*)b)->id); } +/* Create new entry */ +epggrab_channel_t *epggrab_channel_create + ( epggrab_module_t *owner, htsmsg_t *conf, const char *uuid ) +{ + epggrab_channel_t *ec; + + if (htsmsg_get_str(conf, "id") == NULL) + return NULL; + + ec = calloc(1, sizeof(*ec)); + if (idnode_insert(&ec->idnode, uuid, &epggrab_channel_class, 0)) { + if (uuid) + tvherror("epggrab", "invalid uuid '%s'", uuid); + free(ec); + return NULL; + } + + ec->mod = owner; + ec->enabled = 1; + ec->tree = owner->channels; + + if (conf) + idnode_load(&ec->idnode, conf); + + TAILQ_INSERT_TAIL(&epggrab_channel_entries, ec, all_link); + if (RB_INSERT_SORTED(owner->channels, ec, link, _ch_id_cmp)) abort(); + + return ec; +} + /* Find/Create channel in the list */ epggrab_channel_t *epggrab_channel_find ( epggrab_channel_tree_t *tree, const char *id, int create, int *save, @@ -215,28 +268,47 @@ epggrab_channel_t *epggrab_channel_find ec = RB_INSERT_SORTED(tree, epggrab_channel_skel, link, _ch_id_cmp); if (!ec) { assert(owner); - ec = epggrab_channel_skel; + ec = epggrab_channel_skel; SKEL_USED(epggrab_channel_skel); - ec->id = strdup(ec->id); - ec->mod = owner; - *save = 1; + ec->enabled = 1; + ec->tree = tree; + ec->id = strdup(ec->id); + ec->mod = owner; + TAILQ_INSERT_TAIL(&epggrab_channel_entries, ec, all_link); + + if (idnode_insert(&ec->idnode, NULL, &epggrab_channel_class, 0)) + abort(); + *save = 1; return ec; } } return ec; } -void epggrab_channel_destroy - ( epggrab_channel_tree_t *tree, epggrab_channel_t *ec, int delconf ) +void epggrab_channel_save( epggrab_channel_t *ec ) { - epggrab_channel_link_t *ecl; + htsmsg_t *m = htsmsg_create_map(); + idnode_save(&ec->idnode, m); + hts_settings_save(m, "epggrab/%s/channels/%s", + ec->mod->id, idnode_uuid_as_sstr(&ec->idnode)); + htsmsg_destroy(m); +} - if (!ec) return; +void epggrab_channel_destroy( epggrab_channel_t *ec, int delconf ) +{ + if (ec == NULL) return; /* Already linked */ - while ((ecl = LIST_FIRST(&ec->channels)) != NULL) - epggrab_channel_link_delete(ecl, delconf); - RB_REMOVE(tree, ec, link); + epggrab_channel_links_delete(ec, 0); + RB_REMOVE(ec->tree, ec, link); + TAILQ_REMOVE(&epggrab_channel_entries, ec, all_link); + idnode_unlink(&ec->idnode); + + if (delconf) + hts_settings_remove("epggrab/%s/channels/%s", + ec->mod->id, idnode_uuid_as_sstr(&ec->idnode)); + + free(ec->comment); free(ec->name); free(ec->icon); free(ec->id); @@ -247,60 +319,40 @@ void epggrab_channel_flush ( epggrab_channel_tree_t *tree, int delconf ) { epggrab_channel_t *ec; - while ((ec = RB_FIRST(tree)) != NULL) - epggrab_channel_destroy(tree, ec, delconf); + if (tree == NULL) + return; + while ((ec = RB_FIRST(tree)) != NULL) { + assert(tree == ec->tree); + epggrab_channel_destroy(ec, delconf); + } } /* ************************************************************************** * Global routines * *************************************************************************/ -htsmsg_t *epggrab_channel_list ( int ota ) +void epggrab_channel_add ( channel_t *ch ) { - char name[500]; epggrab_module_t *mod; - epggrab_channel_t *ec; - htsmsg_t *e, *m; - m = htsmsg_create_list(); - LIST_FOREACH(mod, &epggrab_modules, link) { - if (!ota && (mod->type == EPGGRAB_OTA)) continue; - if (mod->channels) { - RB_FOREACH(ec, mod->channels, link) { - e = htsmsg_create_map(); - snprintf(name, sizeof(name), "%s|%s", mod->id, ec->id); - htsmsg_add_str(e, "key", name); - snprintf(name, sizeof(name), "%s: %s (%s)", - mod->name, ec->name ?: ec->id, ec->id); - htsmsg_add_str(e, "val", name); - htsmsg_add_msg(m, NULL, e); - } - } - } - return m; -} + epggrab_channel_t *egc; -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); - } + LIST_FOREACH(mod, &epggrab_modules, link) + RB_FOREACH(egc, mod->channels, link) + if (epggrab_channel_match_and_link(egc, ch)) + break; } 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); - } + idnode_list_mapping_t *ilm; + + while ((ilm = LIST_FIRST(&ch->ch_epggrab)) != NULL) + idnode_list_unlink(ilm, 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); - } + return epggrab_channel_add(ch); } const char * @@ -332,8 +384,143 @@ epggrab_channel_is_ota ( epggrab_channel_t *ec ) return ec->mod->type == EPGGRAB_OTA; } +/* + * Class + */ + +static const char * +epggrab_channel_class_get_title(idnode_t *self, const char *lang) +{ + epggrab_channel_t *ec = (epggrab_channel_t*)self; + + snprintf(prop_sbuf, PROP_SBUF_LEN, "%s: %s (%s)", + ec->mod->name, ec->name ?: ec->id, ec->id); + return prop_sbuf; +} + +static void +epggrab_channel_class_save(idnode_t *self) +{ + epggrab_channel_save((epggrab_channel_t *)self); +} + +static void +epggrab_channel_class_delete(idnode_t *self) +{ + epggrab_channel_destroy((epggrab_channel_t *)self, 1); +} + +static const void * +epggrab_channel_class_module_get ( void *obj ) +{ + epggrab_channel_t *ec = obj; + snprintf(prop_sbuf, PROP_SBUF_LEN, "%s", ec->mod->name ?: ""); + return &prop_sbuf_ptr; +} + +static const void * +epggrab_channel_class_channels_get ( void *obj ) +{ + epggrab_channel_t *ec = obj; + return idnode_list_get1(&ec->channels); +} + +static int +epggrab_channel_class_channels_set ( void *obj, const void *p ) +{ + epggrab_channel_t *ec = obj; + return idnode_list_set1(&ec->idnode, &ec->channels, + &channel_class, (htsmsg_t *)p, + epggrab_channel_map); +} + +static char * +epggrab_channel_class_channels_rend ( void *obj, const char *lang ) +{ + epggrab_channel_t *ec = obj; + return idnode_list_get_csv1(&ec->channels, lang); +} + + +const idclass_t epggrab_channel_class = { + .ic_class = "epggrab_channel", + .ic_caption = N_("EPG grabber channel"), + .ic_event = "epggrab_channel", + .ic_perm_def = ACCESS_ADMIN, + .ic_save = epggrab_channel_class_save, + .ic_get_title = epggrab_channel_class_get_title, + .ic_delete = epggrab_channel_class_delete, + .ic_properties = (const property_t[]){ + { + .type = PT_BOOL, + .id = "enabled", + .name = N_("Enabled"), + .off = offsetof(epggrab_channel_t, enabled), + }, + { + .type = PT_STR, + .id = "module", + .name = N_("Module"), + .get = epggrab_channel_class_module_get, + .opts = PO_RDONLY | PO_NOSAVE, + }, + { + .type = PT_STR, + .id = "id", + .name = N_("ID"), + .off = offsetof(epggrab_channel_t, id), + }, + { + .type = PT_STR, + .id = "name", + .name = N_("Name"), + .off = offsetof(epggrab_channel_t, name), + }, + { + .type = PT_S64, + .intsplit = CHANNEL_SPLIT, + .id = "number", + .name = N_("Number"), + .off = offsetof(epggrab_channel_t, lcn), + }, + { + .type = PT_STR, + .id = "icon", + .name = N_("Icon"), + .off = offsetof(epggrab_channel_t, icon), + }, + { + .type = PT_STR, + .islist = 1, + .id = "channels", + .name = N_("Channels"), + .set = epggrab_channel_class_channels_set, + .get = epggrab_channel_class_channels_get, + .list = channel_class_get_list, + .rend = epggrab_channel_class_channels_rend, + }, + { + .type = PT_STR, + .id = "comment", + .name = N_("Comment"), + .off = offsetof(epggrab_channel_t, comment) + }, + {} + } +}; + +/* + * + */ +void +epggrab_channel_init( void ) +{ + TAILQ_INIT(&epggrab_channel_entries); +} + void epggrab_channel_done( void ) { + assert(TAILQ_FIRST(&epggrab_channel_entries) == NULL); SKEL_FREE(epggrab_channel_skel); } diff --git a/src/epggrab/module.c b/src/epggrab/module.c index 540c3259b..d662d2a4c 100644 --- a/src/epggrab/module.c +++ b/src/epggrab/module.c @@ -204,12 +204,6 @@ epggrab_module_t *epggrab_module_create skel->name = strdup(name); skel->priority = priority; 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)); @@ -264,96 +258,15 @@ void epggrab_module_parse( void *m, htsmsg_t *data ) * Module channel routines * *************************************************************************/ -void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch ) -{ - htsmsg_t *a = NULL, *m = htsmsg_create_map(); - epggrab_module_t *mod = _m; - epggrab_channel_link_t *ecl; - - if (ch->name) - htsmsg_add_str(m, "name", ch->name); - if (ch->icon) - htsmsg_add_str(m, "icon", ch->icon); - LIST_FOREACH(ecl, &ch->channels, ecl_epg_link) { - if (!a) a = htsmsg_create_list(); - htsmsg_add_str(a, NULL, channel_get_suuid(ecl->ecl_channel)); - } - if (a) htsmsg_add_msg(m, "channels", a); - if (ch->major) - htsmsg_add_u32(m, "major", ch->major); - if (ch->minor) - htsmsg_add_u32(m, "minor", ch->minor); - - hts_settings_save(m, "epggrab/%s/channels/%s", mod->id, ch->id); - htsmsg_destroy(m); -} - -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_match_and_link(egc, ch)) break; - } -} - -void epggrab_module_ch_rem ( void *m, channel_t *ch ) -{ - epggrab_channel_link_t *ecl; - while ((ecl = LIST_FIRST(&ch->ch_epggrab))) - epggrab_channel_link_delete(ecl, 1); -} - -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; - const char *str; - uint32_t u32; - htsmsg_t *a; - htsmsg_field_t *f; - channel_t *ch; - epggrab_channel_t *egc; - - egc = epggrab_channel_find(mod->channels, id, 1, &save, mod); - - if ((str = htsmsg_get_str(m, "name"))) - egc->name = strdup(str); - if ((str = htsmsg_get_str(m, "icon"))) - egc->icon = strdup(str); - if(!htsmsg_get_u32(m, "major", &u32)) - egc->major = u32; - if(!htsmsg_get_u32(m, "minor", &u32)) - egc->minor = u32; - if ((a = htsmsg_get_list(m, "channels"))) { - HTSMSG_FOREACH(f, a) { - if ((str = htsmsg_field_get_str(f))) { - if ((ch = channel_find_by_uuid(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(egc, ch); - } -} - 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))) { + if ((m = hts_settings_load_r(1, "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); + epggrab_channel_create(mod, e, f->hmf_name); } htsmsg_destroy(m); } diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index 3b61e7ba5..ad53f5f9f 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -443,7 +443,7 @@ opentv_parse_event_section { opentv_module_t *mod = sta->os_mod; epggrab_channel_t *ec; - epggrab_channel_link_t *ecl; + idnode_list_mapping_t *ilm; const char *lang = NULL; int save = 0; @@ -456,8 +456,10 @@ opentv_parse_event_section if (!(ec = _opentv_find_epggrab_channel(mod, cid, 0, NULL))) return 0; /* Iterate all channels */ - LIST_FOREACH(ecl, &ec->channels, ecl_epg_link) - save |= opentv_parse_event_section_one(sta, cid, mjd, ecl->ecl_channel, lang, buf, len); + LIST_FOREACH(ilm, &ec->channels, ilm_in2_link) + save |= opentv_parse_event_section_one(sta, cid, mjd, + (channel_t *)ilm->ilm_in2, + lang, buf, len); /* Update EPG */ if (save) epg_updated(); @@ -476,7 +478,7 @@ opentv_desc_channels opentv_status_t *sta = mt->mt_opaque; opentv_module_t *mod = sta->os_mod; epggrab_channel_t *ec; - epggrab_channel_link_t *ecl; + idnode_list_mapping_t *ilm; mpegts_service_t *svc; channel_t *ch; int sid, cid, cnum, unk; @@ -516,17 +518,17 @@ opentv_desc_channels skip_chnum: if (svc && LIST_FIRST(&svc->s_channels)) { ec =_opentv_find_epggrab_channel(mod, cid, 1, &save); - ecl = LIST_FIRST(&ec->channels); + ilm = LIST_FIRST(&ec->channels); ch = (channel_t *)LIST_FIRST(&svc->s_channels)->ilm_in2; - tvhtrace(mt->mt_name, " ec = %p, ecl = %p", ec, ecl); + tvhtrace(mt->mt_name, " ec = %p, ilm = %p", ec, ilm); - if (ecl && ecl->ecl_channel != ch) { - epggrab_channel_link_delete(ecl, 1); - ecl = NULL; + if (ilm && ilm->ilm_in2 != &ch->ch_id) { + epggrab_channel_link_delete(ec, ch, 1); + ilm = NULL; } - if (!ecl) - epggrab_channel_link(ec, ch); + if (!ilm) + epggrab_channel_link(ec, ch, NULL); save |= epggrab_channel_set_number(ec, cnum, 0); } i += 9; @@ -959,7 +961,6 @@ static int _opentv_prov_load_one ( const char *id, htsmsg_t *m ) 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; _opentv_compile_pattern_list(&mod->p_snum, htsmsg_get_list(m, "season_num")); _opentv_compile_pattern_list(&mod->p_enum, htsmsg_get_list(m, "episode_num")); _opentv_compile_pattern_list(&mod->p_pnum, htsmsg_get_list(m, "part_num")); diff --git a/src/epggrab/module/pyepg.c b/src/epggrab/module/pyepg.c index 3bc63dc51..4834fc75a 100644 --- a/src/epggrab/module/pyepg.c +++ b/src/epggrab/module/pyepg.c @@ -354,7 +354,7 @@ static int _pyepg_parse_schedule htsmsg_field_t *f; epggrab_channel_t *ec; const char *str; - epggrab_channel_link_t *ecl; + idnode_list_mapping_t *ilm; if ( data == NULL ) return 0; @@ -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, ecl_epg_link) + LIST_FOREACH(ilm, &ec->channels, ilm_in2_link) save |= _pyepg_parse_broadcast(mod, htsmsg_get_map_by_field(f), - ecl->ecl_channel, stats); + (channel_t *)ilm->ilm_in2, stats); } } diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c index 35c00018f..2c300c402 100644 --- a/src/epggrab/module/xmltv.c +++ b/src/epggrab/module/xmltv.c @@ -580,21 +580,21 @@ static int _xmltv_parse_programme htsmsg_t *attribs, *tags, *subtag; const char *s, *chid, *icon = NULL; time_t start, stop; - epggrab_channel_t *ch; - epggrab_channel_link_t *ecl; + epggrab_channel_t *ec; + idnode_list_mapping_t *ilm; if(body == NULL) return 0; if((attribs = htsmsg_get_map(body, "attrib")) == NULL) return 0; if((tags = htsmsg_get_map(body, "tags")) == NULL) return 0; if((chid = htsmsg_get_str(attribs, "channel")) == NULL) return 0; - if((ch = _xmltv_channel_find(chid, 1, &chsave)) == NULL) return 0; + if((ec = _xmltv_channel_find(chid, 1, &chsave)) == NULL) return 0; if (chsave) { - epggrab_channel_updated(ch); + epggrab_channel_updated(ec); stats->channels.created++; stats->channels.modified++; } - if (!LIST_FIRST(&ch->channels)) return 0; + if (!LIST_FIRST(&ec->channels)) return 0; if((s = htsmsg_get_str(attribs, "start")) == NULL) return 0; start = _xmltv_str2time(s); if((s = htsmsg_get_str(attribs, "stop")) == NULL) return 0; @@ -606,8 +606,8 @@ static int _xmltv_parse_programme if(stop <= start || stop <= dispatch_clock) return 0; - LIST_FOREACH(ecl, &ch->channels, ecl_epg_link) - save |= _xmltv_parse_programme_tags(mod, ecl->ecl_channel, tags, + LIST_FOREACH(ilm, &ec->channels, ilm_in2_link) + save |= _xmltv_parse_programme_tags(mod, (channel_t *)ilm->ilm_in2, tags, start, stop, icon, stats); return save; } diff --git a/src/epggrab/otamux.c b/src/epggrab/otamux.c index 30920c7a1..b541c0e69 100644 --- a/src/epggrab/otamux.c +++ b/src/epggrab/otamux.c @@ -880,7 +880,6 @@ epggrab_ota_shutdown ( void ) { epggrab_ota_mux_t *ota; - pthread_mutex_lock(&global_lock); epggrab_ota_running = 0; while ((ota = TAILQ_FIRST(&epggrab_ota_active)) != NULL) epggrab_ota_free(&epggrab_ota_active, ota); @@ -888,7 +887,6 @@ epggrab_ota_shutdown ( void ) epggrab_ota_free(&epggrab_ota_pending, ota); while ((ota = RB_FIRST(&epggrab_ota_all)) != NULL) epggrab_ota_free(NULL, ota); - pthread_mutex_unlock(&global_lock); SKEL_FREE(epggrab_ota_mux_skel); SKEL_FREE(epggrab_svc_link_skel); free(epggrab_ota_cron_multi); diff --git a/src/epggrab/private.h b/src/epggrab/private.h index 1e97c25a2..660b74b39 100644 --- a/src/epggrab/private.h +++ b/src/epggrab/private.h @@ -50,15 +50,20 @@ int epggrab_channel_match ( epggrab_channel_t *ec, struct channel *ch ); int epggrab_channel_match_and_link ( epggrab_channel_t *ec, struct channel *ch ); +epggrab_channel_t *epggrab_channel_create + ( epggrab_module_t *owner, htsmsg_t *conf, const char *uuid ); + epggrab_channel_t *epggrab_channel_find ( epggrab_channel_tree_t *chs, const char *id, int create, int *save, epggrab_module_t *owner ); +void epggrab_channel_save ( epggrab_channel_t *ec ); void epggrab_channel_destroy - ( epggrab_channel_tree_t *tree, epggrab_channel_t *ec, int delconf ); + ( epggrab_channel_t *ec, int delconf ); void epggrab_channel_flush ( epggrab_channel_tree_t *tree, int delconf ); +void epggrab_channel_init(void); void epggrab_channel_done(void); /* ************************************************************************** diff --git a/src/prop.c b/src/prop.c index 3eb24627b..aceb7500e 100644 --- a/src/prop.c +++ b/src/prop.c @@ -26,6 +26,7 @@ #include "lang_str.h" char prop_sbuf[PROP_SBUF_LEN]; +char *prop_sbuf_ptr = prop_sbuf; /* ************************************************************************** * Utilities diff --git a/src/prop.h b/src/prop.h index f2f35d152..bd44b94ba 100644 --- a/src/prop.h +++ b/src/prop.h @@ -104,6 +104,7 @@ typedef struct property { #define PROP_SBUF_LEN 4096 extern char prop_sbuf[PROP_SBUF_LEN]; +extern char *prop_sbuf_ptr; const property_t *prop_find(const property_t *p, const char *name); diff --git a/src/service.c b/src/service.c index 1631005ab..f72498d38 100644 --- a/src/service.c +++ b/src/service.c @@ -1722,25 +1722,6 @@ service_mapped(service_t *s) if (s->s_mapped) s->s_mapped(s); } -/* - * Find the primary EPG service (to stop EPG trying to update - * from multiple OTA sources) - */ -#ifdef MOVE_TO_MPEGTS -int -service_is_primary_epg(service_t *svc) -{ - service_t *ret = NULL, *t; - if (!svc || !svc->s_ch) return 0; - LIST_FOREACH(t, &svc->s_ch->ch_services, s_ch_link) { - if (!t->s_is_enabled(t) || !t->s_dvb_eit_enable) continue; - if (!ret) - ret = t; - } - return !ret ? 0 : (ret->s_dvb_service_id == svc->s_dvb_service_id); -} -#endif - /* * list of known service types */ diff --git a/src/webui/static/app/epggrab.js b/src/webui/static/app/epggrab.js index 5686c8b9a..7f94567d1 100644 --- a/src/webui/static/app/epggrab.js +++ b/src/webui/static/app/epggrab.js @@ -33,6 +33,29 @@ tvheadend.epggrab_base = function(panel, index) { } +tvheadend.epggrab_map = function(panel, index) { + + tvheadend.idnode_grid(panel, { + url: 'api/epggrab/channel', + all: 1, + titleS: _('EPG Grabber Channel'), + titleP: _('EPG Grabber Channels'), + iconCls: 'baseconf', + tabIndex: index, + del: true, + sort: { + field: 'name', + direction: 'ASC' + }, + help: function() { + new tvheadend.help(_('EPG Grabber Channels'), 'config_epggrab.html'); + } + }); + + return panel; + +} + tvheadend.epggrab_mod = function(panel, index) { var actions = new Ext.ux.grid.RowActions({ diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index 10cfd5bb1..9060aac22 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -482,6 +482,7 @@ function accessUpdate(o) { tvheadend.channel_tab(chepg); tvheadend.cteditor(chepg); tvheadend.bouquet(chepg); + tvheadend.epggrab_map(chepg); tvheadend.epggrab_base(chepg); tvheadend.epggrab_mod(chepg);