Add system wide scanning of XMLTV grabbers.
Add support for selecting XMLTV grabber from web user interface.
This commit is contained in:
parent
3f2c453ee8
commit
4010f71b77
5 changed files with 681 additions and 15 deletions
|
@ -113,6 +113,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque)
|
|||
extjs_load(hq, "static/app/chconf.js");
|
||||
extjs_load(hq, "static/app/epg.js");
|
||||
extjs_load(hq, "static/app/dvr.js");
|
||||
extjs_load(hq, "static/app/xmltv.js");
|
||||
|
||||
/**
|
||||
* Finally, the app itself
|
||||
|
@ -717,9 +718,8 @@ extjs_xmltv(http_connection_t *hc, const char *remain, void *opaque)
|
|||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
const char *op = http_arg_get(&hc->hc_req_args, "op");
|
||||
xmltv_channel_t *xc;
|
||||
htsmsg_t *out, *array, *e;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
htsmsg_t *out, *array, *e, *r;
|
||||
const char *s;
|
||||
|
||||
if(!strcmp(op, "listChannels")) {
|
||||
|
||||
|
@ -730,21 +730,55 @@ extjs_xmltv(http_connection_t *hc, const char *remain, void *opaque)
|
|||
htsmsg_add_str(e, "xcTitle", "None");
|
||||
htsmsg_add_msg(array, NULL, e);
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
LIST_FOREACH(xc, &xmltv_displaylist, xc_displayname_link) {
|
||||
e = htsmsg_create();
|
||||
htsmsg_add_str(e, "xcTitle", xc->xc_displayname);
|
||||
htsmsg_add_msg(array, NULL, e);
|
||||
}
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
htsmsg_add_msg(out, "entries", array);
|
||||
|
||||
} else if(!strcmp(op, "loadSettings")) {
|
||||
|
||||
pthread_mutex_lock(&xmltv_mutex);
|
||||
r = htsmsg_create();
|
||||
|
||||
if((s = xmltv_get_current_grabber()) != NULL)
|
||||
htsmsg_add_str(r, "grabber", s);
|
||||
|
||||
htsmsg_add_u32(r, "grabinterval", xmltv_grab_interval);
|
||||
pthread_mutex_unlock(&xmltv_mutex);
|
||||
|
||||
out = json_single_record(r, "xmltvSettings");
|
||||
|
||||
} else if(!strcmp(op, "saveSettings")) {
|
||||
|
||||
pthread_mutex_lock(&xmltv_mutex);
|
||||
|
||||
s = http_arg_get(&hc->hc_req_args, "grabber");
|
||||
xmltv_set_current_grabber(s);
|
||||
|
||||
pthread_mutex_unlock(&xmltv_mutex);
|
||||
|
||||
out = htsmsg_create();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else if(!strcmp(op, "listGrabbers")) {
|
||||
|
||||
out = htsmsg_create();
|
||||
|
||||
pthread_mutex_lock(&xmltv_mutex);
|
||||
array = xmltv_list_grabbers();
|
||||
pthread_mutex_unlock(&xmltv_mutex);
|
||||
if(array != NULL)
|
||||
htsmsg_add_msg(out, "entries", array);
|
||||
|
||||
} else {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
http_output_content(hc, "text/x-json; charset=UTF-8");
|
||||
|
@ -1072,7 +1106,6 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* WEB user interface
|
||||
*/
|
||||
|
|
|
@ -102,6 +102,7 @@ tvheadend.app = function() {
|
|||
autoScroll:true,
|
||||
title: 'Configuration',
|
||||
items: [new tvheadend.chconf,
|
||||
new tvheadend.xmltv,
|
||||
new tvheadend.cteditor,
|
||||
new tvheadend.dvrsettings,
|
||||
new tvheadend.dvb,
|
||||
|
@ -126,10 +127,11 @@ tvheadend.app = function() {
|
|||
},new Ext.TabPanel({
|
||||
region:'center',
|
||||
activeTab:0,
|
||||
items:[
|
||||
items:[
|
||||
new tvheadend.epg,
|
||||
new tvheadend.dvr,
|
||||
confpanel]
|
||||
confpanel
|
||||
]
|
||||
})
|
||||
]
|
||||
});
|
||||
|
|
194
webui/static/app/xmltv.js
Normal file
194
webui/static/app/xmltv.js
Normal file
|
@ -0,0 +1,194 @@
|
|||
tvheadend.grabberStore = new Ext.data.JsonStore({
|
||||
root:'entries',
|
||||
fields: ['identifier','name','version','apiconfig'],
|
||||
url:'xmltv',
|
||||
baseParams: {op: 'listGrabbers'},
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
tvheadend.xmltvsetup = function() {
|
||||
|
||||
var deck1info = new Ext.form.Label({
|
||||
fieldLabel: 'Version',
|
||||
html:'',
|
||||
});
|
||||
|
||||
var deck1cb = new Ext.form.ComboBox({
|
||||
loadingText: 'Scanning for XMLTV grabbers, please wait...',
|
||||
fieldLabel: 'XML-TV Source',
|
||||
name: 'xmltvchannel',
|
||||
width: 350,
|
||||
displayField:'name',
|
||||
valueField:'identifier',
|
||||
store: tvheadend.grabberStore,
|
||||
forceSelection: true,
|
||||
editable: false,
|
||||
triggerAction: 'all',
|
||||
mode: 'remote',
|
||||
emptyText: 'Select grabber'
|
||||
});
|
||||
|
||||
var deck1 = new Ext.FormPanel({
|
||||
labelAlign: 'right',
|
||||
labelWidth: 100,
|
||||
bodyStyle: 'padding: 5px',
|
||||
defaultType: 'label',
|
||||
layout: 'form',
|
||||
border:false,
|
||||
items: [deck1cb, deck1info]
|
||||
});
|
||||
|
||||
|
||||
|
||||
var win = new Ext.Window({
|
||||
title: 'Configure XMLTV grabbers',
|
||||
bodyStyle: 'padding: 5px',
|
||||
layout: 'fit',
|
||||
width: 500,
|
||||
height: 500,
|
||||
constrainHeader: true,
|
||||
buttonAlign: 'center',
|
||||
items: [deck1],
|
||||
bbar: [
|
||||
{
|
||||
id: 'move-back',
|
||||
text: 'Back',
|
||||
disabled: true
|
||||
},
|
||||
'->',
|
||||
{
|
||||
id: 'move-next',
|
||||
text: 'Next'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
win.show();
|
||||
|
||||
deck1cb.on('select', function(c,r,i) {
|
||||
deck1info.setText(r.data.version);
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
tvheadend.xmltv = function() {
|
||||
|
||||
var confreader = new Ext.data.JsonReader({
|
||||
root: 'xmltvSettings',
|
||||
}, ['grabber','grabinterval']);
|
||||
|
||||
var grabberSelect = new Ext.form.ComboBox({
|
||||
loadingText: 'Loading, please wait...',
|
||||
fieldLabel: 'XML-TV Source',
|
||||
name: 'grabber',
|
||||
width: 350,
|
||||
displayField:'name',
|
||||
valueField:'identifier',
|
||||
store: tvheadend.grabberStore,
|
||||
forceSelection: true,
|
||||
editable: false,
|
||||
triggerAction: 'all',
|
||||
mode: 'remote',
|
||||
emptyText: 'Select grabber'
|
||||
});
|
||||
|
||||
var confpanel = new Ext.FormPanel({
|
||||
title:'XML TV',
|
||||
border:false,
|
||||
bodyStyle:'padding:15px',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 200,
|
||||
waitMsgTarget: true,
|
||||
reader: confreader,
|
||||
layout: 'form',
|
||||
defaultType: 'textfield',
|
||||
items: [
|
||||
grabberSelect,
|
||||
new Ext.form.NumberField({
|
||||
allowNegative: false,
|
||||
allowDecimals: false,
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
fieldLabel: 'Grab interval (hours)',
|
||||
name: 'grabinterval'
|
||||
})
|
||||
],
|
||||
tbar: [{
|
||||
tooltip: 'Save changes made to configuration below',
|
||||
iconCls:'save',
|
||||
text: "Save configuration",
|
||||
handler: saveChanges
|
||||
}],
|
||||
|
||||
});
|
||||
|
||||
confpanel.on('render', function() {
|
||||
confpanel.getForm().load({
|
||||
url:'/xmltv',
|
||||
params:{'op':'loadSettings'},
|
||||
success:function(form, action) {
|
||||
confpanel.enable();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
grabberSelect.on('select', function(c,r,i) {
|
||||
if(r.data.apiconfig) {
|
||||
|
||||
Ext.MessageBox.confirm('XMLTV',
|
||||
'Configure grabber? ' +
|
||||
'If you know that the grabber is already '+
|
||||
'set up or if you want to configure it '+
|
||||
'manually you may skip this step',
|
||||
function(button) {
|
||||
Ext.MessageBox.alert('XMLTV',
|
||||
'oops, embedded '+
|
||||
'config not '+
|
||||
'implemeted yet');
|
||||
}
|
||||
);
|
||||
|
||||
} else {
|
||||
Ext.MessageBox.alert('XMLTV',
|
||||
'This grabber does not support being ' +
|
||||
'configured from external application ' +
|
||||
'(such as Tvheadend).<br>' +
|
||||
'Make sure that the grabber is properly ' +
|
||||
'configured before saving configuration.<br>'+
|
||||
'<br>' +
|
||||
'To configure manually execute the ' +
|
||||
'following command in a shell on the ' +
|
||||
'server:<br>' +
|
||||
'$ ' + r.data.identifier +
|
||||
' --configure<br>' +
|
||||
'<br>' +
|
||||
'Note: It is important to configure the ' +
|
||||
'grabber using the same userid as tvheadend '+
|
||||
'since most grabbers save their '+
|
||||
'configuration in the users home directory.'+
|
||||
'<br>' +
|
||||
'<br>' +
|
||||
'Grabber version: ' + r.data.version
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function saveChanges() {
|
||||
confpanel.getForm().submit({
|
||||
url:'/xmltv',
|
||||
params:{'op':'saveSettings'},
|
||||
waitMsg:'Saving Data...',
|
||||
failure: function(form, action) {
|
||||
Ext.Msg.alert('Save failed', action.result.errormsg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return confpanel;
|
||||
}
|
||||
|
442
xmltv.c
442
xmltv.c
|
@ -17,11 +17,13 @@
|
|||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <libhts/htsmsg_xml.h>
|
||||
#include <libhts/htssettings.h>
|
||||
|
@ -32,11 +34,47 @@
|
|||
#include "xmltv.h"
|
||||
#include "spawn.h"
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
LIST_HEAD(xmltv_grabber_list, xmltv_grabber);
|
||||
static struct xmltv_grabber_list xmltv_grabbers;
|
||||
|
||||
typedef struct xmltv_grabber {
|
||||
LIST_ENTRY(xmltv_grabber) xg_link;
|
||||
char *xg_path;
|
||||
time_t xg_mtime;
|
||||
|
||||
char *xg_version;
|
||||
char *xg_description;
|
||||
|
||||
uint32_t xg_capabilities;
|
||||
#define XG_CAP_BASELINE 0x1
|
||||
#define XG_CAP_MANUALCONFIG 0x2
|
||||
#define XG_CAP_CACHE 0x4
|
||||
#define XG_CAP_APICONFIG 0x8
|
||||
|
||||
int xg_dirty;
|
||||
|
||||
} xmltv_grabber_t;
|
||||
|
||||
static xmltv_grabber_t *xg_current;
|
||||
|
||||
|
||||
uint32_t xmltv_grab_interval;
|
||||
|
||||
/* xmltv_channels is protected by global_lock */
|
||||
static struct xmltv_channel_tree xmltv_channels;
|
||||
struct xmltv_channel_list xmltv_displaylist;
|
||||
|
||||
pthread_mutex_t xmltv_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t xmltv_cond;
|
||||
static int xmltv_confver;
|
||||
|
||||
static void xmltv_grabbers_index(void);
|
||||
static void xmltv_grabbers_load(void);
|
||||
static void xmltv_grabbers_save(void);
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -382,16 +420,13 @@ xmltv_parse(htsmsg_t *body)
|
|||
*
|
||||
*/
|
||||
static void
|
||||
xmltv_grab(void)
|
||||
xmltv_grab(const char *prog)
|
||||
{
|
||||
const char *prog = "/usr/bin/tv_grab_se_swedb";
|
||||
int outlen;
|
||||
char *outbuf;
|
||||
htsmsg_t *body;
|
||||
char errbuf[100];
|
||||
|
||||
tvhlog(LOG_INFO, "xmltv", "Starting grabber \"%s\"", prog);
|
||||
|
||||
outlen = spawn_and_store_stdout(prog, NULL, &outbuf);
|
||||
if(outlen < 1) {
|
||||
tvhlog(LOG_ERR, "xmltv", "No output from \"%s\"", prog);
|
||||
|
@ -419,9 +454,50 @@ xmltv_grab(void)
|
|||
static void *
|
||||
xmltv_thread(void *aux)
|
||||
{
|
||||
int confver = 0;
|
||||
struct timespec ts;
|
||||
char *p;
|
||||
|
||||
pthread_mutex_lock(&xmltv_mutex);
|
||||
|
||||
xmltv_grabbers_load();
|
||||
|
||||
xmltv_grabbers_index();
|
||||
|
||||
ts.tv_nsec = 0;
|
||||
ts.tv_sec = 0;
|
||||
|
||||
while(1) {
|
||||
xmltv_grab();
|
||||
sleep(3600);
|
||||
|
||||
while(confver == xmltv_confver) {
|
||||
|
||||
if(xg_current == NULL) {
|
||||
pthread_cond_wait(&xmltv_cond, &xmltv_mutex);
|
||||
continue;
|
||||
}
|
||||
if(pthread_cond_timedwait(&xmltv_cond, &xmltv_mutex, &ts) == ETIMEDOUT)
|
||||
break;
|
||||
}
|
||||
|
||||
ts.tv_sec = time(NULL) + xmltv_grab_interval * 3600;
|
||||
|
||||
confver = xmltv_confver;
|
||||
|
||||
if(xg_current != NULL) {
|
||||
tvhlog(LOG_INFO, "xmltv",
|
||||
"Grabbing \"%s\" using command \"%s\"",
|
||||
xg_current->xg_description, xg_current->xg_path);
|
||||
|
||||
/* Dup path so we can unlock */
|
||||
p = strdup(xg_current->xg_path);
|
||||
|
||||
pthread_mutex_unlock(&xmltv_mutex);
|
||||
|
||||
xmltv_grab(p);
|
||||
|
||||
pthread_mutex_lock(&xmltv_mutex);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,6 +509,360 @@ void
|
|||
xmltv_init(void)
|
||||
{
|
||||
pthread_t ptid;
|
||||
xmltv_grab_interval = 12; /* Default half a day */
|
||||
|
||||
/* Load all channels */
|
||||
xmltv_load();
|
||||
|
||||
pthread_create(&ptid, NULL, xmltv_thread, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
xmltv_grabber_destroy(xmltv_grabber_t *xg)
|
||||
{
|
||||
LIST_REMOVE(xg, xg_link);
|
||||
free(xg->xg_path);
|
||||
free(xg->xg_version);
|
||||
free(xg->xg_description);
|
||||
free(xg);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static char *
|
||||
xmltv_grabber_get_description(const char *prog)
|
||||
{
|
||||
int i, outlen;
|
||||
char *outbuf;
|
||||
const char *args[] = {prog, "--description", NULL};
|
||||
|
||||
if((outlen = spawn_and_store_stdout(prog, (void *)args, &outbuf)) < 1)
|
||||
return NULL;
|
||||
|
||||
for(i = 0; i < outlen; i++)
|
||||
if(outbuf[i] < 32)
|
||||
outbuf[i] = 0;
|
||||
return outbuf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static char *
|
||||
xmltv_grabber_get_version(const char *prog)
|
||||
{
|
||||
int outlen;
|
||||
char *outbuf;
|
||||
const char *args[] = {prog, "--version", NULL};
|
||||
|
||||
if((outlen = spawn_and_store_stdout(prog, (void *)args, &outbuf)) < 1)
|
||||
return NULL;
|
||||
return outbuf;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
xmltv_grabber_get_capabilities(const char *prog)
|
||||
{
|
||||
int outlen;
|
||||
char *outbuf;
|
||||
int r;
|
||||
|
||||
const char *args[] = {prog, "--capabilities", NULL};
|
||||
|
||||
if((outlen = spawn_and_store_stdout(prog, (void *)args, &outbuf)) < 1)
|
||||
return 0;
|
||||
|
||||
r = strstr(outbuf, "baseline") ? XG_CAP_BASELINE : 0;
|
||||
r |= strstr(outbuf, "manualconfig") ? XG_CAP_MANUALCONFIG : 0;
|
||||
r |= strstr(outbuf, "cache") ? XG_CAP_CACHE : 0;
|
||||
r |= strstr(outbuf, "apiconfig") ? XG_CAP_APICONFIG : 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
xg_desc_cmp(const xmltv_grabber_t *a, const xmltv_grabber_t *b)
|
||||
{
|
||||
return strcmp(a->xg_description, b->xg_description);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
xmltv_grabber_add(const char *path, time_t mtime)
|
||||
{
|
||||
char *desc;
|
||||
xmltv_grabber_t *xg;
|
||||
int wasthisgrabber = 0;
|
||||
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
|
||||
if(!strcmp(xg->xg_path, path))
|
||||
break;
|
||||
|
||||
if(xg != NULL) {
|
||||
if(xg->xg_mtime == mtime) {
|
||||
xg->xg_dirty = 0;
|
||||
return 0; /* Already there */
|
||||
}
|
||||
wasthisgrabber = xg == xg_current;
|
||||
xmltv_grabber_destroy(xg);
|
||||
xg_current = NULL;
|
||||
}
|
||||
if((desc = xmltv_grabber_get_description(path)) == NULL)
|
||||
return 0; /* Probably not a working grabber */
|
||||
|
||||
xg = calloc(1, sizeof(xmltv_grabber_t));
|
||||
xg->xg_path = strdup(path);
|
||||
xg->xg_mtime = mtime;
|
||||
xg->xg_description = strdup(desc);
|
||||
xg->xg_capabilities = xmltv_grabber_get_capabilities(path);
|
||||
tvh_str_set(&xg->xg_version, xmltv_grabber_get_version(path));
|
||||
LIST_INSERT_SORTED(&xmltv_grabbers, xg, xg_link, xg_desc_cmp);
|
||||
if(wasthisgrabber)
|
||||
xg_current = xg;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static xmltv_grabber_t *
|
||||
xmltv_grabber_find_by_path(const char *path)
|
||||
{
|
||||
xmltv_grabber_t *xg;
|
||||
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
|
||||
if(!strcmp(xg->xg_path, path))
|
||||
break;
|
||||
return xg;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
xmltv_grabber_filter(const struct dirent *d)
|
||||
{
|
||||
if(strncmp(d->d_name, "tv_grab_", 8))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
xmltv_scan_grabbers(const char *path)
|
||||
{
|
||||
struct dirent **namelist;
|
||||
int n, change = 0;
|
||||
char fname[300];
|
||||
struct stat st;
|
||||
|
||||
if((n = scandir(path, &namelist, xmltv_grabber_filter, NULL)) < 0)
|
||||
return 0;
|
||||
|
||||
while(n--) {
|
||||
snprintf(fname, sizeof(fname), "%s/%s", path, namelist[n]->d_name);
|
||||
if(!stat(fname, &st))
|
||||
change |= xmltv_grabber_add(fname, st.st_mtime);
|
||||
}
|
||||
free(namelist);
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
xmltv_grabbers_index(void)
|
||||
{
|
||||
xmltv_grabber_t *xg, *next;
|
||||
int change;
|
||||
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
|
||||
xg->xg_dirty = 1;
|
||||
|
||||
change = xmltv_scan_grabbers("/bin");
|
||||
change |= xmltv_scan_grabbers("/usr/bin");
|
||||
change |= xmltv_scan_grabbers("/usr/local/bin");
|
||||
|
||||
for(xg = LIST_FIRST(&xmltv_grabbers); xg != NULL; xg = next) {
|
||||
next = LIST_NEXT(xg, xg_link);
|
||||
if(xg->xg_dirty) {
|
||||
change = 1;
|
||||
xmltv_grabber_destroy(xg);
|
||||
}
|
||||
}
|
||||
|
||||
if(change)
|
||||
xmltv_grabbers_save();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
xmltv_grabbers_load(void)
|
||||
{
|
||||
xmltv_grabber_t *xg;
|
||||
htsmsg_t *l, *c, *m;
|
||||
htsmsg_field_t *f;
|
||||
const char *path, *desc;
|
||||
uint32_t u32;
|
||||
|
||||
if((m = hts_settings_load("xmltv/config")) == NULL)
|
||||
return;
|
||||
|
||||
htsmsg_get_u32(m, "grab-interval", &xmltv_grab_interval);
|
||||
|
||||
if((l = htsmsg_get_array(m, "grabbers")) != NULL) {
|
||||
|
||||
HTSMSG_FOREACH(f, l) {
|
||||
if((c = htsmsg_get_msg_by_field(f)) == NULL)
|
||||
continue;
|
||||
|
||||
path = htsmsg_get_str(c, "path");
|
||||
desc = htsmsg_get_str(c, "description");
|
||||
if(path == NULL || desc == NULL)
|
||||
continue;
|
||||
|
||||
xg = calloc(1, sizeof(xmltv_grabber_t));
|
||||
xg->xg_path = strdup(path);
|
||||
xg->xg_description = strdup(desc);
|
||||
|
||||
if(!htsmsg_get_u32(c, "mtime", &u32))
|
||||
xg->xg_mtime = u32;
|
||||
|
||||
tvh_str_set(&xg->xg_version, htsmsg_get_str(c, "version"));
|
||||
htsmsg_get_u32(c, "capabilities", &xg->xg_capabilities);
|
||||
LIST_INSERT_SORTED(&xmltv_grabbers, xg, xg_link, xg_desc_cmp);
|
||||
}
|
||||
}
|
||||
|
||||
if((path = htsmsg_get_str(m, "current-grabber")) != NULL) {
|
||||
if((xg = xmltv_grabber_find_by_path(path)) != NULL)
|
||||
xg_current = xg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
xmltv_grabbers_save(void)
|
||||
{
|
||||
htsmsg_t *array, *e, *m;
|
||||
xmltv_grabber_t *xg;
|
||||
|
||||
m = htsmsg_create();
|
||||
|
||||
array = htsmsg_create_array();
|
||||
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link) {
|
||||
e = htsmsg_create();
|
||||
htsmsg_add_str(e, "path", xg->xg_path);
|
||||
htsmsg_add_str(e, "description", xg->xg_description);
|
||||
if(xg->xg_version != NULL)
|
||||
htsmsg_add_str(e, "version", xg->xg_version);
|
||||
htsmsg_add_u32(e, "mtime", xg->xg_mtime);
|
||||
htsmsg_add_u32(e, "capabilities", xg->xg_capabilities);
|
||||
|
||||
htsmsg_add_msg(array, NULL, e);
|
||||
}
|
||||
|
||||
htsmsg_add_msg(m, "grabbers", array);
|
||||
|
||||
htsmsg_add_u32(m, "grab-interval", xmltv_grab_interval);
|
||||
if(xg_current != NULL)
|
||||
htsmsg_add_str(m, "current-grabber", xg_current->xg_path);
|
||||
|
||||
hts_settings_save(m, "xmltv/config");
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
htsmsg_t *
|
||||
xmltv_list_grabbers(void)
|
||||
{
|
||||
htsmsg_t *array, *m;
|
||||
xmltv_grabber_t *xg;
|
||||
|
||||
lock_assert(&xmltv_mutex);
|
||||
|
||||
xmltv_grabbers_index();
|
||||
|
||||
array = htsmsg_create_array();
|
||||
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link) {
|
||||
m = htsmsg_create();
|
||||
htsmsg_add_str(m, "identifier", xg->xg_path);
|
||||
htsmsg_add_str(m, "name", xg->xg_description);
|
||||
htsmsg_add_str(m, "version", xg->xg_version ?: "Unknown version");
|
||||
htsmsg_add_u32(m, "apiconfig", !!(xg->xg_capabilities & XG_CAP_APICONFIG));
|
||||
htsmsg_add_msg(array, NULL, m);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const char *
|
||||
xmltv_get_current_grabber(void)
|
||||
{
|
||||
lock_assert(&xmltv_mutex);
|
||||
return xg_current ? xg_current->xg_description : NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
xmltv_set_current_grabber(const char *desc)
|
||||
{
|
||||
xmltv_grabber_t *xg;
|
||||
|
||||
lock_assert(&xmltv_mutex);
|
||||
|
||||
if(desc != NULL) {
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
|
||||
if(!strcmp(xg->xg_description, desc))
|
||||
break;
|
||||
if(xg == NULL)
|
||||
return;
|
||||
} else {
|
||||
xg = NULL;
|
||||
}
|
||||
|
||||
if(xg_current == xg)
|
||||
return;
|
||||
|
||||
xg_current = xg;
|
||||
|
||||
xmltv_confver++;
|
||||
pthread_cond_signal(&xmltv_cond);
|
||||
xmltv_grabbers_save();
|
||||
}
|
||||
|
|
7
xmltv.h
7
xmltv.h
|
@ -46,6 +46,13 @@ xmltv_channel_t *xmltv_channel_find(const char *id, int create);
|
|||
|
||||
xmltv_channel_t *xmltv_channel_find_by_displayname(const char *name);
|
||||
|
||||
htsmsg_t *xmltv_list_grabbers(void);
|
||||
|
||||
const char *xmltv_get_current_grabber(void);
|
||||
void xmltv_set_current_grabber(const char *path);
|
||||
|
||||
extern struct xmltv_channel_list xmltv_displaylist;
|
||||
extern uint32_t xmltv_grab_interval;
|
||||
extern pthread_mutex_t xmltv_mutex;
|
||||
|
||||
#endif /* EPG_XMLTV_H__ */
|
||||
|
|
Loading…
Add table
Reference in a new issue