#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
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 },
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 *
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);
/* 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;
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;
#include "avahi.h"
#include "url.h"
#include "satip/server.h"
+#include "channels.h"
#include <netinet/ip.h>
}
}
+/*
+ * 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);
+ }
+}
+
/*
config_migrate_v19,
config_migrate_v20,
config_migrate_v21,
- config_migrate_v22
+ config_migrate_v22,
+ config_migrate_v23
};
/*
pthread_mutex_init(&epggrab_mutex, NULL);
pthread_cond_init(&epggrab_cond, NULL);
+ epggrab_channel_init();
+
/* Initialise modules */
#if ENABLE_MPEGTS
eit_init();
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();
free(epggrab_conf.ota_cron);
epggrab_conf.ota_cron = NULL;
epggrab_channel_done();
+ pthread_mutex_unlock(&global_lock);
}
typedef struct epggrab_module_list epggrab_module_list_t;
struct mpegts_mux;
+struct channel;
/* **************************************************************************
* Grabber 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
* 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 );
/* 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 );
};
/*
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
/*
* 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
#include <assert.h>
#include <string.h>
+struct epggrab_channel_queue epggrab_channel_entries;
+
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;
/* 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;
/* 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;
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 */
((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,
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);
( 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 *
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);
}
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));
* 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);
}
{
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;
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();
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;
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;
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"));
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;
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);
}
}
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;
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;
}
{
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);
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);
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);
/* **************************************************************************
#include "lang_str.h"
char prop_sbuf[PROP_SBUF_LEN];
+char *prop_sbuf_ptr = prop_sbuf;
/* **************************************************************************
* Utilities
#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);
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
*/
}
+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({
tvheadend.channel_tab(chepg);
tvheadend.cteditor(chepg);
tvheadend.bouquet(chepg);
+ tvheadend.epggrab_map(chepg);
tvheadend.epggrab_base(chepg);
tvheadend.epggrab_mod(chepg);