commit
06782f719c
105 changed files with 8021 additions and 5497 deletions
3
Makefile
3
Makefile
|
@ -156,7 +156,8 @@ SRCS += \
|
|||
src/api/api_imagecache.c \
|
||||
src/api/api_esfilter.c \
|
||||
src/api/api_intlconv.c \
|
||||
src/api/api_access.c
|
||||
src/api/api_access.c \
|
||||
src/api/api_dvr.c
|
||||
|
||||
SRCS += \
|
||||
src/parsers/parsers.c \
|
||||
|
|
|
@ -68,13 +68,11 @@ The columns have the following functions:
|
|||
<dd>
|
||||
Enables access to all video recording functions. This also include administration of the auto recordings.
|
||||
|
||||
<dt>All Configs (VR)
|
||||
<dt>DVR Config Profile
|
||||
<dd>
|
||||
Allow to use all DVR configuration profiles. If not set, a DVR
|
||||
configuration profile matched to the authorized user by name is always used
|
||||
(the configuration profile must have same name as the user). If the DVR
|
||||
configuration profile does not exists, the default profile is used.
|
||||
The user is also not allowed to select another profile.
|
||||
If set, the user will only be able to use the DVR config profile
|
||||
equal to this value.
|
||||
Note that this field is unset when the DVR Config Profile is removed.
|
||||
|
||||
<dt>Web interface
|
||||
<dd>
|
||||
|
@ -84,13 +82,6 @@ The columns have the following functions:
|
|||
<dd>
|
||||
Enables access to the Configuration tab.
|
||||
|
||||
<dt>Username Channel Tag Match
|
||||
<dd>
|
||||
If enabled, the user will only be able to access channels with a tag the
|
||||
same name as the username.
|
||||
|
||||
This provides a very rudimentary way of limiting access to certain channels.
|
||||
|
||||
<dt>Min Channel Num
|
||||
<dd>
|
||||
If nonzero, the user will only be able to access channels with
|
||||
|
@ -103,11 +94,9 @@ The columns have the following functions:
|
|||
|
||||
<dt>Channel Tag
|
||||
<dd>
|
||||
If set, the user will only be able to access channels with
|
||||
a channel tag equal to this value. Note that this field stores
|
||||
the tag name (not identified). It means that this field would not be
|
||||
updated when the tag name is changed. A manual re-set of this field
|
||||
is required (for security reasons).
|
||||
If set, the user will only be able to access channels containing
|
||||
this channel tag.
|
||||
Note that this field is unset when the channel tag is removed.
|
||||
|
||||
<dt>Comment
|
||||
<dd>
|
||||
|
|
10
docs/html/config_dvrauto.html
Normal file
10
docs/html/config_dvrauto.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<div class="hts-doc-text">
|
||||
|
||||
<p>
|
||||
This tab is used to manipulate with the Digital Video Recorder entries -
|
||||
the automatic recording.
|
||||
|
||||
<p>
|
||||
A volunteer required to fill this...
|
||||
|
||||
</div>
|
9
docs/html/config_dvrlog.html
Normal file
9
docs/html/config_dvrlog.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<div class="hts-doc-text">
|
||||
|
||||
<p>
|
||||
This tab is used to manipulate with the Digital Video Recorder entries.
|
||||
|
||||
<p>
|
||||
A volunteer required to fill this...
|
||||
|
||||
</div>
|
|
@ -17,7 +17,40 @@
|
|||
<dd>Whether or not the mux is enabled and thus available.
|
||||
|
||||
<dt>EPG
|
||||
<dd>Whether or not to retrieve EPG information from the mux.
|
||||
<dd>EPG scan setup
|
||||
|
||||
<dl>
|
||||
<dt>Disable
|
||||
<dd>Disable the EPG scan
|
||||
|
||||
<dt>Enable (auto)
|
||||
<dd>Enable the EPG scan (when some services from this mux are assigned to channels)
|
||||
|
||||
<dt>Force (auto)
|
||||
<dd>Force the EPG scan (everytime when the EPG scan is triggered)
|
||||
|
||||
<dt>Only EIT
|
||||
<dd>Do only EIT EPG scan (when some services from this mux are assigned to channels)
|
||||
|
||||
<dt>Only UK Freesat
|
||||
<dd>Do only UK Freesat EPG scan (when some services from this mux are assigned to channels)
|
||||
|
||||
<dt>Only UK Freeview
|
||||
<dd>Do only UK Freeview EPG scan (when some services from this mux are assigned to channels)
|
||||
|
||||
<dt>Only Viasat Baltic
|
||||
<dd>Do only Viasat Baltic EPG scan (when some services from this mux are assigned to channels)
|
||||
|
||||
<dt>Only OpenTV Sky UK
|
||||
<dd>Do only OpenTV Sky UK EPG scan (the TSID must match in the skyuk configuration file)
|
||||
|
||||
<dt>Only OpenTV Sky Italia
|
||||
<dd>Do only OpenTV Sky Italia EPG scan (the TSID must match in the skyit configuration file)
|
||||
|
||||
<dt>Only OpenTV Sky Ausat
|
||||
<dd>Do only OpenTV Sky Ausat EPG scan (the TSID must match in the ausat configuration file)
|
||||
|
||||
</dl>
|
||||
|
||||
<dt>Network
|
||||
<dd>The name of the network to which the mux belongs. Networks are defined in Configuration -> DVB Inputs -> Networks - DVB-S or ATSC, for example.
|
||||
|
@ -45,6 +78,11 @@
|
|||
|
||||
<dt>Character Set
|
||||
<dd>The character encoding for this mux (e.g. UTF-8).
|
||||
|
||||
<dt>PMT Descriptor 0x06 = AC-3
|
||||
<dd>Whether or not the empty PMT descriptor 0x06 defaults to
|
||||
the AC-3 stream. Some Chinese cable providers are using
|
||||
this. If unsure, keep this off.
|
||||
|
||||
<dt>Interface
|
||||
<dd>IPTV : the network interface/card on which the IPTV source can be found.
|
||||
|
|
162
src/access.c
162
src/access.c
|
@ -37,6 +37,8 @@
|
|||
#include "access.h"
|
||||
#include "settings.h"
|
||||
#include "channels.h"
|
||||
#include "dvr/dvr.h"
|
||||
#include "tcp.h"
|
||||
|
||||
struct access_entry_queue access_entries;
|
||||
struct access_ticket_queue access_tickets;
|
||||
|
@ -158,6 +160,11 @@ access_ticket_verify(const char *id, const char *resource)
|
|||
void
|
||||
access_destroy(access_t *a)
|
||||
{
|
||||
if (a == NULL)
|
||||
return;
|
||||
free(a->aa_username);
|
||||
free(a->aa_representative);
|
||||
htsmsg_destroy(a->aa_dvrcfgs);
|
||||
htsmsg_destroy(a->aa_chtags);
|
||||
free(a);
|
||||
}
|
||||
|
@ -292,10 +299,16 @@ access_update(access_t *a, access_entry_t *ae)
|
|||
}
|
||||
}
|
||||
|
||||
if(ae->ae_chtag && ae->ae_chtag[0] != '\0') {
|
||||
if(ae->ae_dvr_config && ae->ae_dvr_config->dvr_config_name[0] != '\0') {
|
||||
if (a->aa_dvrcfgs == NULL)
|
||||
a->aa_dvrcfgs = htsmsg_create_list();
|
||||
htsmsg_add_str(a->aa_dvrcfgs, NULL, idnode_uuid_as_str(&ae->ae_dvr_config->dvr_id));
|
||||
}
|
||||
|
||||
if(ae->ae_chtag && ae->ae_chtag->ct_name[0] != '\0') {
|
||||
if (a->aa_chtags == NULL)
|
||||
a->aa_chtags = htsmsg_create_list();
|
||||
htsmsg_add_str(a->aa_chtags, NULL, ae->ae_chtag);
|
||||
htsmsg_add_str(a->aa_chtags, NULL, idnode_uuid_as_str(&ae->ae_chtag->ct_id));
|
||||
}
|
||||
|
||||
a->aa_rights |= ae->ae_rights;
|
||||
|
@ -310,6 +323,14 @@ access_get(const char *username, const char *password, struct sockaddr *src)
|
|||
access_t *a = calloc(1, sizeof(*a));
|
||||
access_entry_t *ae;
|
||||
|
||||
if (username) {
|
||||
a->aa_username = strdup(username);
|
||||
a->aa_representative = strdup(username);
|
||||
} else {
|
||||
a->aa_representative = malloc(50);
|
||||
tcp_get_ip_str((struct sockaddr*)src, a->aa_representative, 50);
|
||||
}
|
||||
|
||||
if (access_noacl) {
|
||||
a->aa_rights = ACCESS_FULL;
|
||||
return a;
|
||||
|
@ -546,12 +567,8 @@ access_entry_update_rights(access_entry_t *ae)
|
|||
r |= ACCESS_ADVANCED_STREAMING;
|
||||
if (ae->ae_dvr)
|
||||
r |= ACCESS_RECORDER;
|
||||
if (ae->ae_dvrallcfg)
|
||||
r |= ACCESS_RECORDER_ALL;
|
||||
if (ae->ae_webui)
|
||||
r |= ACCESS_WEB_INTERFACE;
|
||||
if (ae->ae_tag_only)
|
||||
r |= ACCESS_TAG_ONLY;
|
||||
if (ae->ae_admin)
|
||||
r |= ACCESS_ADMIN;
|
||||
ae->ae_rights = r;
|
||||
|
@ -569,6 +586,7 @@ access_entry_create(const char *uuid, htsmsg_t *conf)
|
|||
{
|
||||
access_ipmask_t *ai;
|
||||
access_entry_t *ae, *ae2;
|
||||
const char *s;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
|
@ -585,6 +603,9 @@ access_entry_create(const char *uuid, htsmsg_t *conf)
|
|||
|
||||
if (conf) {
|
||||
idnode_load(&ae->ae_id, conf);
|
||||
/* note password has PO_NOSAVE, thus it must be set manually */
|
||||
if ((s = htsmsg_get_str(conf, "password")) != NULL)
|
||||
access_entry_class_password_set(ae, s);
|
||||
access_entry_update_rights(ae);
|
||||
TAILQ_FOREACH(ae2, &access_entries, ae_link)
|
||||
if (ae->ae_index < ae2->ae_index)
|
||||
|
@ -628,6 +649,11 @@ access_entry_destroy(access_entry_t *ae)
|
|||
TAILQ_REMOVE(&access_entries, ae, ae_link);
|
||||
idnode_unlink(&ae->ae_id);
|
||||
|
||||
if (ae->ae_dvr_config)
|
||||
LIST_REMOVE(ae, ae_dvr_config_link);
|
||||
if (ae->ae_chtag)
|
||||
LIST_REMOVE(ae, ae_channel_tag_link);
|
||||
|
||||
while((ai = TAILQ_FIRST(&ae->ae_ipmasks)) != NULL)
|
||||
{
|
||||
TAILQ_REMOVE(&ae->ae_ipmasks, ai, ai_link);
|
||||
|
@ -642,10 +668,42 @@ access_entry_destroy(access_entry_t *ae)
|
|||
free(ae);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
access_destroy_by_dvr_config(dvr_config_t *cfg, int delconf)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
|
||||
while ((ae = LIST_FIRST(&cfg->dvr_accesses)) != NULL) {
|
||||
LIST_REMOVE(ae, ae_dvr_config_link);
|
||||
ae->ae_dvr_config = NULL;
|
||||
if (delconf)
|
||||
access_entry_save(ae);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
access_destroy_by_channel_tag(channel_tag_t *ct, int delconf)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
|
||||
while ((ae = LIST_FIRST(&ct->ct_accesses)) != NULL) {
|
||||
LIST_REMOVE(ae, ae_channel_tag_link);
|
||||
ae->ae_chtag = NULL;
|
||||
if (delconf)
|
||||
access_entry_save(ae);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
void
|
||||
access_entry_save(access_entry_t *ae)
|
||||
{
|
||||
htsmsg_t *c = htsmsg_create_map();
|
||||
|
@ -795,19 +853,72 @@ access_entry_class_password2_set(void *o, const void *v)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
access_entry_chtag_list ( void *o )
|
||||
static int
|
||||
access_entry_chtag_set(void *o, const void *v)
|
||||
{
|
||||
channel_tag_t *ct;
|
||||
htsmsg_t *m = htsmsg_create_list();
|
||||
TAILQ_FOREACH(ct, &channel_tags, ct_link)
|
||||
htsmsg_add_str(m, NULL, ct->ct_name);
|
||||
return m;
|
||||
access_entry_t *ae = (access_entry_t *)o;
|
||||
channel_tag_t *tag = v ? channel_tag_find_by_uuid(v) : NULL;
|
||||
if (tag == NULL && ae->ae_chtag) {
|
||||
LIST_REMOVE(ae, ae_channel_tag_link);
|
||||
ae->ae_chtag = NULL;
|
||||
return 1;
|
||||
} else if (ae->ae_chtag != tag) {
|
||||
if (ae->ae_chtag)
|
||||
LIST_REMOVE(ae, ae_channel_tag_link);
|
||||
ae->ae_chtag = tag;
|
||||
LIST_INSERT_HEAD(&tag->ct_accesses, ae, ae_channel_tag_link);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const void *
|
||||
access_entry_chtag_get(void *o)
|
||||
{
|
||||
static const char *ret;
|
||||
access_entry_t *ae = (access_entry_t *)o;
|
||||
if (ae->ae_chtag)
|
||||
ret = idnode_uuid_as_str(&ae->ae_chtag->ct_id);
|
||||
else
|
||||
ret = "";
|
||||
return &ret;
|
||||
}
|
||||
|
||||
static int
|
||||
access_entry_dvr_config_set(void *o, const void *v)
|
||||
{
|
||||
access_entry_t *ae = (access_entry_t *)o;
|
||||
dvr_config_t *cfg = v ? dvr_config_find_by_uuid(v) : NULL;
|
||||
if (cfg == NULL && ae->ae_dvr_config) {
|
||||
LIST_REMOVE(ae, ae_dvr_config_link);
|
||||
ae->ae_dvr_config = NULL;
|
||||
return 1;
|
||||
} else if (ae->ae_dvr_config != cfg) {
|
||||
if (ae->ae_dvr_config)
|
||||
LIST_REMOVE(ae, ae_dvr_config_link);
|
||||
ae->ae_dvr_config = cfg;
|
||||
LIST_INSERT_HEAD(&cfg->dvr_accesses, ae, ae_dvr_config_link);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const void *
|
||||
access_entry_dvr_config_get(void *o)
|
||||
{
|
||||
static const char *ret;
|
||||
access_entry_t *ae = (access_entry_t *)o;
|
||||
if (ae->ae_dvr_config)
|
||||
ret = idnode_uuid_as_str(&ae->ae_dvr_config->dvr_id);
|
||||
else
|
||||
ret = "";
|
||||
return &ret;
|
||||
}
|
||||
|
||||
const idclass_t access_entry_class = {
|
||||
.ic_class = "access",
|
||||
.ic_caption = "Access",
|
||||
.ic_event = "access",
|
||||
.ic_save = access_entry_class_save,
|
||||
.ic_get_title = access_entry_class_get_title,
|
||||
.ic_delete = access_entry_class_delete,
|
||||
|
@ -875,10 +986,13 @@ const idclass_t access_entry_class = {
|
|||
.off = offsetof(access_entry_t, ae_dvr),
|
||||
},
|
||||
{
|
||||
.type = PT_BOOL,
|
||||
.id = "dvrallcfg",
|
||||
.name = "All Configs (VR)",
|
||||
.off = offsetof(access_entry_t, ae_dvrallcfg),
|
||||
.type = PT_STR,
|
||||
.id = "dvr_config",
|
||||
.name = "DVR Config Profile",
|
||||
.set = access_entry_dvr_config_set,
|
||||
.get = access_entry_dvr_config_get,
|
||||
.list = dvr_entry_class_config_name_list,
|
||||
.off = offsetof(access_entry_t, ae_dvr_config),
|
||||
},
|
||||
{
|
||||
.type = PT_BOOL,
|
||||
|
@ -892,12 +1006,6 @@ const idclass_t access_entry_class = {
|
|||
.name = "Admin",
|
||||
.off = offsetof(access_entry_t, ae_admin),
|
||||
},
|
||||
{
|
||||
.type = PT_BOOL,
|
||||
.id = "tag_only",
|
||||
.name = "Username Channel Tag Match",
|
||||
.off = offsetof(access_entry_t, ae_tag_only),
|
||||
},
|
||||
{
|
||||
.type = PT_U32,
|
||||
.id = "channel_min",
|
||||
|
@ -915,7 +1023,9 @@ const idclass_t access_entry_class = {
|
|||
.id = "channel_tag",
|
||||
.name = "Channel Tag",
|
||||
.off = offsetof(access_entry_t, ae_chtag),
|
||||
.list = access_entry_chtag_list,
|
||||
.set = access_entry_chtag_set,
|
||||
.get = access_entry_chtag_get,
|
||||
.list = channel_tag_class_get_list,
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
|
@ -956,7 +1066,7 @@ access_init(int createdefault, int noacl)
|
|||
TAILQ_INIT(&access_tickets);
|
||||
|
||||
/* Load */
|
||||
if ((c = hts_settings_load_r(1, "accesscontrol")) != NULL) {
|
||||
if ((c = hts_settings_load("accesscontrol")) != NULL) {
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(m = htsmsg_field_get_map(f))) continue;
|
||||
(void)access_entry_create(f->hmf_name, m);
|
||||
|
|
43
src/access.h
43
src/access.h
|
@ -22,6 +22,9 @@
|
|||
#include "idnode.h"
|
||||
#include "htsmsg.h"
|
||||
|
||||
struct dvr_config;
|
||||
struct channel_tag;
|
||||
|
||||
typedef struct access_ipmask {
|
||||
TAILQ_ENTRY(access_ipmask) ai_link;
|
||||
|
||||
|
@ -48,18 +51,25 @@ typedef struct access_entry {
|
|||
char *ae_password;
|
||||
char *ae_password2;
|
||||
char *ae_comment;
|
||||
|
||||
int ae_index;
|
||||
int ae_enabled;
|
||||
|
||||
int ae_streaming;
|
||||
int ae_adv_streaming;
|
||||
|
||||
int ae_dvr;
|
||||
int ae_dvrallcfg;
|
||||
struct dvr_config *ae_dvr_config;
|
||||
LIST_ENTRY(access_entry) ae_dvr_config_link;
|
||||
|
||||
int ae_webui;
|
||||
int ae_admin;
|
||||
int ae_tag_only;
|
||||
|
||||
uint32_t ae_chmin;
|
||||
uint32_t ae_chmax;
|
||||
char *ae_chtag;
|
||||
|
||||
struct channel_tag *ae_chtag;
|
||||
LIST_ENTRY(access_entry) ae_channel_tag_link;
|
||||
|
||||
uint32_t ae_rights;
|
||||
|
||||
|
@ -82,7 +92,10 @@ typedef struct access_ticket {
|
|||
} access_ticket_t;
|
||||
|
||||
typedef struct access {
|
||||
char *aa_username;
|
||||
char *aa_representative;
|
||||
uint32_t aa_rights;
|
||||
htsmsg_t *aa_dvrcfgs;
|
||||
uint32_t aa_chmin;
|
||||
uint32_t aa_chmax;
|
||||
htsmsg_t *aa_chtags;
|
||||
|
@ -94,14 +107,11 @@ typedef struct access {
|
|||
#define ACCESS_ADVANCED_STREAMING (1<<1)
|
||||
#define ACCESS_WEB_INTERFACE (1<<2)
|
||||
#define ACCESS_RECORDER (1<<3)
|
||||
#define ACCESS_RECORDER_ALL (1<<4)
|
||||
#define ACCESS_TAG_ONLY (1<<5)
|
||||
#define ACCESS_ADMIN (1<<6)
|
||||
#define ACCESS_ADMIN (1<<4)
|
||||
|
||||
#define ACCESS_FULL \
|
||||
(ACCESS_STREAMING | ACCESS_ADVANCED_STREAMING | \
|
||||
ACCESS_WEB_INTERFACE | ACCESS_RECORDER | \
|
||||
ACCESS_RECORDER_ALL | ACCESS_ADMIN)
|
||||
ACCESS_WEB_INTERFACE | ACCESS_RECORDER | ACCESS_ADMIN)
|
||||
|
||||
/**
|
||||
* Create a new ticket for the requested resource and generate a id for it
|
||||
|
@ -129,6 +139,9 @@ void access_destroy(access_t *a);
|
|||
int access_verify(const char *username, const char *password,
|
||||
struct sockaddr *src, uint32_t mask);
|
||||
|
||||
static inline int access_verify2(access_t *a, uint32_t mask)
|
||||
{ return (a->aa_rights & mask) == mask ? 0 : -1; }
|
||||
|
||||
/**
|
||||
* Get the access structure
|
||||
*/
|
||||
|
@ -154,6 +167,20 @@ access_get_by_addr(struct sockaddr *src);
|
|||
access_entry_t *
|
||||
access_entry_create(const char *uuid, htsmsg_t *conf);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
access_entry_save(access_entry_t *ae);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
access_destroy_by_dvr_config(struct dvr_config *cfg, int delconf);
|
||||
void
|
||||
access_destroy_by_channel_tag(struct channel_tag *ct, int delconf);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
11
src/api.c
11
src/api.c
|
@ -61,7 +61,8 @@ api_register_all ( const api_hook_t *hooks )
|
|||
}
|
||||
|
||||
int
|
||||
api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp )
|
||||
api_exec ( access_t *perm, const char *subsystem,
|
||||
htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
api_hook_t h;
|
||||
api_link_t *ah, skel;
|
||||
|
@ -83,6 +84,9 @@ api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp )
|
|||
return ENOSYS; // TODO: is this really the right error code?
|
||||
}
|
||||
|
||||
if (access_verify2(perm, ah->hook->ah_access))
|
||||
return EPERM;
|
||||
|
||||
/* Extract method */
|
||||
op = htsmsg_get_str(args, "method");
|
||||
if (!op)
|
||||
|
@ -90,12 +94,12 @@ api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp )
|
|||
// Note: this is not required (so no final validation)
|
||||
|
||||
/* Execute */
|
||||
return ah->hook->ah_callback(ah->hook->ah_opaque, op, args, resp);
|
||||
return ah->hook->ah_callback(perm, ah->hook->ah_opaque, op, args, resp);
|
||||
}
|
||||
|
||||
static int
|
||||
api_serverinfo
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
*resp = htsmsg_create_map();
|
||||
htsmsg_add_str(*resp, "sw_version", tvheadend_version);
|
||||
|
@ -128,6 +132,7 @@ void api_init ( void )
|
|||
api_esfilter_init();
|
||||
api_intlconv_init();
|
||||
api_access_init();
|
||||
api_dvr_init();
|
||||
}
|
||||
|
||||
void api_done ( void )
|
||||
|
|
25
src/api.h
25
src/api.h
|
@ -23,15 +23,17 @@
|
|||
#include "htsmsg.h"
|
||||
#include "idnode.h"
|
||||
#include "redblack.h"
|
||||
#include "access.h"
|
||||
|
||||
#define TVH_API_VERSION 12
|
||||
#define TVH_API_VERSION 14
|
||||
|
||||
/*
|
||||
* Command hook
|
||||
*/
|
||||
|
||||
typedef int (*api_callback_t)
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
|
||||
( access_t *perm, void *opaque, const char *op,
|
||||
htsmsg_t *args, htsmsg_t **resp );
|
||||
|
||||
typedef struct api_hook
|
||||
{
|
||||
|
@ -50,7 +52,8 @@ void api_register_all ( const api_hook_t *hooks );
|
|||
/*
|
||||
* Execute
|
||||
*/
|
||||
int api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp );
|
||||
int api_exec ( access_t *perm, const char *subsystem,
|
||||
htsmsg_t *args, htsmsg_t **resp );
|
||||
|
||||
/*
|
||||
* Initialise
|
||||
|
@ -70,6 +73,7 @@ void api_imagecache_init ( void );
|
|||
void api_esfilter_init ( void );
|
||||
void api_intlconv_init ( void );
|
||||
void api_access_init ( void );
|
||||
void api_dvr_init ( void );
|
||||
|
||||
/*
|
||||
* IDnode
|
||||
|
@ -84,21 +88,24 @@ typedef struct api_idnode_grid_conf
|
|||
} api_idnode_grid_conf_t;
|
||||
|
||||
typedef void (*api_idnode_grid_callback_t)
|
||||
(idnode_set_t*, api_idnode_grid_conf_t*, htsmsg_t *args);
|
||||
(access_t *perm, idnode_set_t*, api_idnode_grid_conf_t*, htsmsg_t *args);
|
||||
typedef idnode_set_t *(*api_idnode_tree_callback_t)
|
||||
(void);
|
||||
(access_t *perm);
|
||||
|
||||
int api_idnode_grid
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
|
||||
|
||||
int api_idnode_class
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
|
||||
|
||||
int api_idnode_tree
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
|
||||
|
||||
int api_idnode_load_by_class
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
|
||||
|
||||
int api_idnode_handler
|
||||
( access_t *perm, htsmsg_t *args, htsmsg_t **resp, void (*handler)(access_t *perm, idnode_t *in) );
|
||||
|
||||
/*
|
||||
* Service mapper
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
static void
|
||||
api_access_entry_grid
|
||||
( idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
access_entry_t *ae;
|
||||
|
||||
|
@ -33,15 +33,17 @@ api_access_entry_grid
|
|||
|
||||
static int
|
||||
api_access_entry_create
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
htsmsg_t *conf;
|
||||
access_entry_t *ae;
|
||||
|
||||
if (!(conf = htsmsg_get_map(args, "conf")))
|
||||
return EINVAL;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
access_entry_create(NULL, conf);
|
||||
if ((ae = access_entry_create(NULL, conf)) != NULL)
|
||||
access_entry_save(ae);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
// TODO: this will need converting to an idnode system
|
||||
static int
|
||||
api_channel_list
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
channel_t *ch;
|
||||
htsmsg_t *l, *e;
|
||||
|
@ -50,7 +50,7 @@ api_channel_list
|
|||
|
||||
static void
|
||||
api_channel_grid
|
||||
( idnode_set_t *ins, api_idnode_grid_conf_t *conf )
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf )
|
||||
{
|
||||
channel_t *ch;
|
||||
|
||||
|
@ -60,7 +60,7 @@ api_channel_grid
|
|||
|
||||
static int
|
||||
api_channel_create
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
htsmsg_t *conf;
|
||||
channel_t *ch;
|
||||
|
@ -79,7 +79,7 @@ api_channel_create
|
|||
|
||||
static int
|
||||
api_channel_tag_list
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
channel_tag_t *ct;
|
||||
htsmsg_t *l, *e;
|
||||
|
@ -98,7 +98,7 @@ api_channel_tag_list
|
|||
|
||||
static void
|
||||
api_channel_tag_grid
|
||||
( idnode_set_t *ins, api_idnode_grid_conf_t *conf )
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf )
|
||||
{
|
||||
channel_tag_t *ct;
|
||||
|
||||
|
@ -108,7 +108,7 @@ api_channel_tag_grid
|
|||
|
||||
static int
|
||||
api_channel_tag_create
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
htsmsg_t *conf;
|
||||
channel_tag_t *ct;
|
||||
|
|
358
src/api/api_dvr.c
Normal file
358
src/api/api_dvr.c
Normal file
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* API - DVR
|
||||
*
|
||||
* Copyright (C) 2014 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "tvheadend.h"
|
||||
#include "dvr/dvr.h"
|
||||
#include "epg.h"
|
||||
#include "api.h"
|
||||
|
||||
static const char *
|
||||
api_dvr_config_name( access_t *perm, const char *config_uuid )
|
||||
{
|
||||
dvr_config_t *cfg = NULL;
|
||||
htsmsg_field_t *f;
|
||||
const char *uuid;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if (perm->aa_dvrcfgs == NULL)
|
||||
return config_uuid; /* no change */
|
||||
|
||||
config_uuid = config_uuid ?: "";
|
||||
HTSMSG_FOREACH(f, perm->aa_dvrcfgs) {
|
||||
uuid = htsmsg_field_get_str(f) ?: "";
|
||||
if (strcmp(uuid, config_uuid) == 0)
|
||||
return config_uuid;
|
||||
if (!cfg)
|
||||
cfg = dvr_config_find_by_uuid(uuid);
|
||||
}
|
||||
|
||||
if (!cfg && perm->aa_username)
|
||||
tvhlog(LOG_INFO, "dvr", "User '%s' has no valid dvr config in ACL, using default...", perm->aa_username);
|
||||
|
||||
return cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
api_dvr_config_grid
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
dvr_config_t *cfg;
|
||||
|
||||
LIST_FOREACH(cfg, &dvrconfigs, config_link)
|
||||
if (!idnode_perm((idnode_t *)cfg, perm, NULL))
|
||||
idnode_set_add(ins, (idnode_t*)cfg, &conf->filter);
|
||||
}
|
||||
|
||||
static int
|
||||
api_dvr_config_create
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
dvr_config_t *cfg;
|
||||
htsmsg_t *conf;
|
||||
const char *s;
|
||||
|
||||
if (!(conf = htsmsg_get_map(args, "conf")))
|
||||
return EINVAL;
|
||||
if (!(s = htsmsg_get_str(conf, "name")))
|
||||
return EINVAL;
|
||||
if (s[0] == '\0')
|
||||
return EINVAL;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
if ((cfg = dvr_config_create(NULL, NULL, conf)))
|
||||
dvr_config_save(cfg);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_dvr_entry_finished(dvr_entry_t *entry)
|
||||
{
|
||||
dvr_entry_sched_state_t state = entry->de_sched_state;
|
||||
return state == DVR_COMPLETED && !entry->de_last_error && dvr_get_filesize(entry) != -1;
|
||||
}
|
||||
|
||||
static int is_dvr_entry_upcoming(dvr_entry_t *entry)
|
||||
{
|
||||
dvr_entry_sched_state_t state = entry->de_sched_state;
|
||||
return state == DVR_RECORDING || state == DVR_SCHEDULED;
|
||||
}
|
||||
|
||||
static int is_dvr_entry_failed(dvr_entry_t *entry)
|
||||
{
|
||||
if (is_dvr_entry_finished(entry))
|
||||
return 0;
|
||||
if (is_dvr_entry_upcoming(entry))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
api_dvr_entry_grid
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
|
||||
LIST_FOREACH(de, &dvrentries, de_global_link)
|
||||
idnode_set_add(ins, (idnode_t*)de, &conf->filter);
|
||||
}
|
||||
|
||||
static void
|
||||
api_dvr_entry_grid_upcoming
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
|
||||
LIST_FOREACH(de, &dvrentries, de_global_link)
|
||||
if (is_dvr_entry_upcoming(de))
|
||||
idnode_set_add(ins, (idnode_t*)de, &conf->filter);
|
||||
}
|
||||
|
||||
static void
|
||||
api_dvr_entry_grid_finished
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
|
||||
LIST_FOREACH(de, &dvrentries, de_global_link)
|
||||
if (is_dvr_entry_finished(de))
|
||||
idnode_set_add(ins, (idnode_t*)de, &conf->filter);
|
||||
}
|
||||
|
||||
static void
|
||||
api_dvr_entry_grid_failed
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
|
||||
LIST_FOREACH(de, &dvrentries, de_global_link)
|
||||
if (is_dvr_entry_failed(de))
|
||||
idnode_set_add(ins, (idnode_t*)de, &conf->filter);
|
||||
}
|
||||
|
||||
static int
|
||||
api_dvr_entry_create
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
htsmsg_t *conf;
|
||||
const char *s1, *s2;
|
||||
|
||||
if (!(conf = htsmsg_get_map(args, "conf")))
|
||||
return EINVAL;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
s1 = htsmsg_get_str(conf, "config_name");
|
||||
s2 = api_dvr_config_name(perm, s1);
|
||||
if (strcmp(s1 ?: "", s2 ?: ""))
|
||||
htsmsg_set_str(conf, "config_name", s2 ?: "");
|
||||
|
||||
if (perm->aa_representative)
|
||||
htsmsg_set_str(conf, "creator", perm->aa_representative);
|
||||
|
||||
if ((de = dvr_entry_create(NULL, conf)))
|
||||
dvr_entry_save(de);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
api_dvr_entry_create_from_single(htsmsg_t *args)
|
||||
{
|
||||
htsmsg_t *entries, *m;
|
||||
const char *s1, *s2;
|
||||
|
||||
if (!(s1 = htsmsg_get_str(args, "config_uuid")))
|
||||
return NULL;
|
||||
if (!(s2 = htsmsg_get_str(args, "event_id")))
|
||||
return NULL;
|
||||
entries = htsmsg_create_list();
|
||||
m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "config_uuid", s1);
|
||||
htsmsg_add_str(m, "event_id", s2);
|
||||
htsmsg_add_msg(entries, NULL, m);
|
||||
return entries;
|
||||
}
|
||||
|
||||
static int
|
||||
api_dvr_entry_create_by_event
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
const char *config_uuid;
|
||||
epg_broadcast_t *e;
|
||||
htsmsg_t *entries, *entries2 = NULL, *m;
|
||||
htsmsg_field_t *f;
|
||||
const char *s;
|
||||
int count = 0;
|
||||
|
||||
if (!(entries = htsmsg_get_list(args, "entries"))) {
|
||||
entries = entries2 = api_dvr_entry_create_from_single(args);
|
||||
if (!entries)
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
HTSMSG_FOREACH(f, entries) {
|
||||
if (!(m = htsmsg_get_map_by_field(f))) continue;
|
||||
|
||||
if (!(config_uuid = htsmsg_get_str(m, "config_uuid")))
|
||||
continue;
|
||||
if (!(s = htsmsg_get_str(m, "event_id")))
|
||||
continue;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
if ((e = epg_broadcast_find_by_id(atoi(s), NULL))) {
|
||||
de = dvr_entry_create_by_event(api_dvr_config_name(perm, config_uuid),
|
||||
e, 0, 0, perm->aa_representative,
|
||||
NULL, DVR_PRIO_NORMAL);
|
||||
if (de)
|
||||
dvr_entry_save(de);
|
||||
}
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
count++;
|
||||
}
|
||||
|
||||
htsmsg_destroy(entries2);
|
||||
|
||||
return !count ? EINVAL : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
api_dvr_cancel(access_t *perm, idnode_t *self)
|
||||
{
|
||||
dvr_entry_cancel((dvr_entry_t *)self);
|
||||
}
|
||||
|
||||
static int
|
||||
api_dvr_entry_cancel
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
return api_idnode_handler(perm, args, resp, api_dvr_cancel);
|
||||
}
|
||||
|
||||
static void
|
||||
api_dvr_autorec_grid
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
dvr_autorec_entry_t *dae;
|
||||
|
||||
TAILQ_FOREACH(dae, &autorec_entries, dae_link)
|
||||
idnode_set_add(ins, (idnode_t*)dae, &conf->filter);
|
||||
}
|
||||
|
||||
static int
|
||||
api_dvr_autorec_create
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
htsmsg_t *conf;
|
||||
dvr_autorec_entry_t *dae;
|
||||
|
||||
if (!(conf = htsmsg_get_map(args, "conf")))
|
||||
return EINVAL;
|
||||
|
||||
if (perm->aa_representative)
|
||||
htsmsg_set_str(conf, "creator", perm->aa_representative);
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
dae = dvr_autorec_create(NULL, conf);
|
||||
if (dae) {
|
||||
dvr_autorec_save(dae);
|
||||
dvr_autorec_changed(dae, 1);
|
||||
}
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
api_dvr_autorec_create_by_series
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
dvr_autorec_entry_t *dae;
|
||||
epg_broadcast_t *e;
|
||||
htsmsg_t *entries, *entries2 = NULL, *m;
|
||||
htsmsg_field_t *f;
|
||||
const char *config_uuid, *s;
|
||||
int count = 0;
|
||||
|
||||
if (!(entries = htsmsg_get_list(args, "entries"))) {
|
||||
entries = entries2 = api_dvr_entry_create_from_single(args);
|
||||
if (!entries)
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
HTSMSG_FOREACH(f, entries) {
|
||||
if (!(m = htsmsg_get_map_by_field(f))) continue;
|
||||
|
||||
if (!(config_uuid = htsmsg_get_str(m, "config_uuid")))
|
||||
continue;
|
||||
if (!(s = htsmsg_get_str(m, "event_id")))
|
||||
continue;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
if ((e = epg_broadcast_find_by_id(atoi(s), NULL))) {
|
||||
dae = dvr_autorec_add_series_link(api_dvr_config_name(perm, config_uuid),
|
||||
e, perm->aa_representative,
|
||||
"Created from EPG query");
|
||||
if (dae) {
|
||||
dvr_autorec_save(dae);
|
||||
dvr_autorec_changed(dae, 1);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
count++;
|
||||
}
|
||||
|
||||
htsmsg_destroy(entries2);
|
||||
|
||||
return !count ? EINVAL : 0;
|
||||
}
|
||||
|
||||
void api_dvr_init ( void )
|
||||
{
|
||||
static api_hook_t ah[] = {
|
||||
{ "dvr/config/class", ACCESS_RECORDER, api_idnode_class, (void*)&dvr_config_class },
|
||||
{ "dvr/config/grid", ACCESS_RECORDER, api_idnode_grid, api_dvr_config_grid },
|
||||
{ "dvr/config/create", ACCESS_ADMIN, api_dvr_config_create, NULL },
|
||||
|
||||
{ "dvr/entry/class", ACCESS_RECORDER, api_idnode_class, (void*)&dvr_entry_class },
|
||||
{ "dvr/entry/grid", ACCESS_RECORDER, api_idnode_grid, api_dvr_entry_grid },
|
||||
{ "dvr/entry/grid_upcoming", ACCESS_RECORDER, api_idnode_grid, api_dvr_entry_grid_upcoming },
|
||||
{ "dvr/entry/grid_finished", ACCESS_RECORDER, api_idnode_grid, api_dvr_entry_grid_finished },
|
||||
{ "dvr/entry/grid_failed", ACCESS_RECORDER, api_idnode_grid, api_dvr_entry_grid_failed },
|
||||
{ "dvr/entry/create", ACCESS_RECORDER, api_dvr_entry_create, NULL },
|
||||
{ "dvr/entry/create_by_event", ACCESS_RECORDER, api_dvr_entry_create_by_event, NULL },
|
||||
{ "dvr/entry/cancel", ACCESS_RECORDER, api_dvr_entry_cancel, NULL },
|
||||
|
||||
{ "dvr/autorec/class", ACCESS_RECORDER, api_idnode_class, (void*)&dvr_autorec_entry_class },
|
||||
{ "dvr/autorec/grid", ACCESS_RECORDER, api_idnode_grid, api_dvr_autorec_grid },
|
||||
{ "dvr/autorec/create", ACCESS_RECORDER, api_dvr_autorec_create, NULL },
|
||||
{ "dvr/autorec/create_by_series", ACCESS_RECORDER, api_dvr_autorec_create_by_series, NULL },
|
||||
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
api_register_all(ah);
|
||||
}
|
|
@ -112,7 +112,7 @@ api_epg_entry ( epg_broadcast_t *eb, const char *lang )
|
|||
|
||||
/* Recording */
|
||||
if ((de = dvr_entry_find_by_event(eb)))
|
||||
htsmsg_add_u32(m, "dvrId", de->de_id);
|
||||
htsmsg_add_str(m, "dvrId", idnode_uuid_as_str(&de->de_id));
|
||||
|
||||
/* Next event */
|
||||
if ((eb = epg_broadcast_get_next(eb)))
|
||||
|
@ -123,7 +123,7 @@ api_epg_entry ( epg_broadcast_t *eb, const char *lang )
|
|||
|
||||
static int
|
||||
api_epg_grid
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int i;
|
||||
epg_query_result_t eqr;
|
||||
|
@ -176,10 +176,25 @@ api_epg_grid
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
api_epg_content_type_list(access_t *perm, void *opaque, const char *op,
|
||||
htsmsg_t *args, htsmsg_t **resp)
|
||||
{
|
||||
htsmsg_t *array;
|
||||
|
||||
*resp = htsmsg_create_map();
|
||||
array = epg_genres_list_all(1, 0);
|
||||
htsmsg_add_msg(*resp, "entries", array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void api_epg_init ( void )
|
||||
{
|
||||
static api_hook_t ah[] = {
|
||||
{ "epg/grid", ACCESS_ANONYMOUS, api_epg_grid, NULL },
|
||||
{ "epg/data/grid", ACCESS_ANONYMOUS, api_epg_grid, NULL },
|
||||
{ "epg/content_type/list", ACCESS_ANONYMOUS, api_epg_content_type_list, NULL },
|
||||
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
static int
|
||||
api_epggrab_channel_list
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
htsmsg_t *m;
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
static void
|
||||
api_esfilter_grid
|
||||
( idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args,
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args,
|
||||
esfilter_class_t cls )
|
||||
{
|
||||
esfilter_t *esf;
|
||||
|
@ -37,7 +37,7 @@ api_esfilter_grid
|
|||
|
||||
static int
|
||||
api_esfilter_create
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp,
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp,
|
||||
esfilter_class_t cls )
|
||||
{
|
||||
htsmsg_t *conf;
|
||||
|
@ -54,11 +54,11 @@ api_esfilter_create
|
|||
|
||||
#define ESFILTER(func, t) \
|
||||
static void api_esfilter_grid_##func \
|
||||
( idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args ) \
|
||||
{ return api_esfilter_grid(ins, conf, args, (t)); } \
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args ) \
|
||||
{ return api_esfilter_grid(perm, ins, conf, args, (t)); } \
|
||||
static int api_esfilter_create_##func \
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) \
|
||||
{ return api_esfilter_create(opaque, op, args, resp, (t)); }
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) \
|
||||
{ return api_esfilter_create(perm, opaque, op, args, resp, (t)); }
|
||||
|
||||
ESFILTER(video, ESF_CLASS_VIDEO);
|
||||
ESFILTER(audio, ESF_CLASS_AUDIO);
|
||||
|
|
|
@ -23,6 +23,30 @@
|
|||
#include "htsmsg.h"
|
||||
#include "api.h"
|
||||
|
||||
static htsmsg_t *
|
||||
api_idnode_flist_conf( htsmsg_t *args, const char *name )
|
||||
{
|
||||
htsmsg_t *m = NULL;
|
||||
const char *s = htsmsg_get_str(args, name);
|
||||
char *r, *saveptr;
|
||||
if (s && s[0] != '\0') {
|
||||
s = r = strdup(s);
|
||||
r = strtok_r(r, ",;:", &saveptr);
|
||||
while (r) {
|
||||
while (*r != '\0' && *r <= ' ')
|
||||
r++;
|
||||
if (*r != '\0') {
|
||||
if (m == NULL)
|
||||
m = htsmsg_create_map();
|
||||
htsmsg_add_bool(m, r, 1);
|
||||
}
|
||||
r = strtok_r(NULL, ",;:", &saveptr);
|
||||
}
|
||||
free((char *)s);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static struct strtab filtcmptab[] = {
|
||||
{ "gt", IC_GT },
|
||||
{ "lt", IC_LT },
|
||||
|
@ -33,7 +57,7 @@ static void
|
|||
api_idnode_grid_conf
|
||||
( htsmsg_t *args, api_idnode_grid_conf_t *conf )
|
||||
{
|
||||
htsmsg_field_t *f;
|
||||
htsmsg_field_t *f, *f2;
|
||||
htsmsg_t *filter, *e;
|
||||
const char *str;
|
||||
|
||||
|
@ -60,11 +84,20 @@ api_idnode_grid_conf
|
|||
if ((v = htsmsg_get_str(e, "value")))
|
||||
idnode_filter_add_str(&conf->filter, k, v, IC_RE);
|
||||
} else if (!strcmp(t, "numeric")) {
|
||||
uint32_t v;
|
||||
if (!htsmsg_get_u32(e, "value", &v)) {
|
||||
int t = str2val(htsmsg_get_str(e, "comparison") ?: "",
|
||||
filtcmptab);
|
||||
idnode_filter_add_num(&conf->filter, k, v, t == -1 ? IC_EQ : t);
|
||||
f2 = htsmsg_field_find(e, "value");
|
||||
if (f2) {
|
||||
int t = str2val(htsmsg_get_str(e, "comparison") ?: "", filtcmptab);
|
||||
if (f2->hmf_type == HMF_DBL) {
|
||||
double dbl;
|
||||
if (!htsmsg_field_get_dbl(f2, &dbl))
|
||||
idnode_filter_add_dbl(&conf->filter, k, dbl, t == -1 ? IC_EQ : t);
|
||||
} else {
|
||||
int64_t v;
|
||||
int64_t intsplit = 0;
|
||||
htsmsg_get_s64(e, "intsplit", &intsplit);
|
||||
if (!htsmsg_field_get_s64(f2, &v))
|
||||
idnode_filter_add_num(&conf->filter, k, v, t == -1 ? IC_EQ : t, intsplit);
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(t, "boolean")) {
|
||||
uint32_t v;
|
||||
|
@ -87,10 +120,11 @@ api_idnode_grid_conf
|
|||
|
||||
int
|
||||
api_idnode_grid
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int i;
|
||||
htsmsg_t *list, *e;
|
||||
htsmsg_t *flist = api_idnode_flist_conf(args, "list");
|
||||
api_idnode_grid_conf_t conf = { 0 };
|
||||
idnode_set_t ins = { 0 };
|
||||
api_idnode_grid_callback_t cb = opaque;
|
||||
|
@ -100,7 +134,7 @@ api_idnode_grid
|
|||
|
||||
/* Create list */
|
||||
pthread_mutex_lock(&global_lock);
|
||||
cb(&ins, &conf, args);
|
||||
cb(perm, &ins, &conf, args);
|
||||
|
||||
/* Sort */
|
||||
if (conf.sort.key)
|
||||
|
@ -111,7 +145,7 @@ api_idnode_grid
|
|||
for (i = conf.start; i < ins.is_count && conf.limit != 0; i++) {
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_str(e, "uuid", idnode_uuid_as_str(ins.is_array[i]));
|
||||
idnode_read0(ins.is_array[i], e, 0);
|
||||
idnode_read0(ins.is_array[i], e, flist, 0);
|
||||
htsmsg_add_msg(list, NULL, e);
|
||||
if (conf.limit > 0) conf.limit--;
|
||||
}
|
||||
|
@ -126,13 +160,14 @@ api_idnode_grid
|
|||
/* Cleanup */
|
||||
free(ins.is_array);
|
||||
idnode_filter_clear(&conf.filter);
|
||||
htsmsg_destroy(flist);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
api_idnode_load_by_class
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int i, _enum;
|
||||
const idclass_t *idc;
|
||||
|
@ -154,6 +189,9 @@ api_idnode_load_by_class
|
|||
for (i = 0; i < is->is_count; i++) {
|
||||
in = is->is_array[i];
|
||||
|
||||
if (idnode_perm(in, perm, NULL))
|
||||
continue;
|
||||
|
||||
/* Name/UUID only */
|
||||
if (_enum) {
|
||||
e = htsmsg_create_map();
|
||||
|
@ -161,8 +199,11 @@ api_idnode_load_by_class
|
|||
htsmsg_add_str(e, "val", idnode_get_title(in));
|
||||
|
||||
/* Full record */
|
||||
} else
|
||||
e = idnode_serialize(in);
|
||||
} else {
|
||||
htsmsg_t *flist = api_idnode_flist_conf(args, "list");
|
||||
e = idnode_serialize0(in, flist, 0);
|
||||
htsmsg_destroy(flist);
|
||||
}
|
||||
|
||||
if (e)
|
||||
htsmsg_add_msg(l, NULL, e);
|
||||
|
@ -180,11 +221,12 @@ api_idnode_load_by_class
|
|||
|
||||
static int
|
||||
api_idnode_load
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int err = 0;
|
||||
int err = 0, meta = 0, count = 0;
|
||||
idnode_t *in;
|
||||
htsmsg_t *uuids, *l = NULL;
|
||||
htsmsg_t *uuids, *l = NULL, *m;
|
||||
htsmsg_t *flist;
|
||||
htsmsg_field_t *f;
|
||||
const char *uuid, *class;
|
||||
|
||||
|
@ -197,7 +239,7 @@ api_idnode_load
|
|||
if (!idc)
|
||||
return EINVAL;
|
||||
// TODO: bit naff that 2 locks are required here
|
||||
return api_idnode_load_by_class((void*)idc, NULL, args, resp);
|
||||
return api_idnode_load_by_class(perm, (void*)idc, NULL, args, resp);
|
||||
}
|
||||
|
||||
/* UUIDs */
|
||||
|
@ -206,6 +248,9 @@ api_idnode_load
|
|||
if (!(uuids = htsmsg_field_get_list(f)))
|
||||
if (!(uuid = htsmsg_field_get_str(f)))
|
||||
return EINVAL;
|
||||
htsmsg_get_s32(args, "meta", &meta);
|
||||
|
||||
flist = api_idnode_flist_conf(args, "list");
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
|
@ -215,16 +260,34 @@ api_idnode_load
|
|||
HTSMSG_FOREACH(f, uuids) {
|
||||
if (!(uuid = htsmsg_field_get_str(f))) continue;
|
||||
if (!(in = idnode_find(uuid, NULL))) continue;
|
||||
htsmsg_add_msg(l, NULL, idnode_serialize(in));
|
||||
if (idnode_perm(in, perm, NULL)) {
|
||||
err = EPERM;
|
||||
continue;
|
||||
}
|
||||
m = idnode_serialize0(in, flist, 0);
|
||||
if (meta > 0)
|
||||
htsmsg_add_msg(m, "meta", idclass_serialize0(in->in_class, flist, 0));
|
||||
htsmsg_add_msg(l, NULL, m);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count)
|
||||
err = 0;
|
||||
|
||||
/* Single */
|
||||
} else {
|
||||
if (!(in = idnode_find(uuid, NULL)))
|
||||
err = ENOENT;
|
||||
else {
|
||||
l = htsmsg_create_list();
|
||||
htsmsg_add_msg(l, NULL, idnode_serialize(in));
|
||||
if (idnode_perm(in, perm, NULL)) {
|
||||
err = EPERM;
|
||||
} else {
|
||||
l = htsmsg_create_list();
|
||||
m = idnode_serialize0(in, flist, 0);
|
||||
if (meta > 0)
|
||||
htsmsg_add_msg(m, "meta", idclass_serialize0(in->in_class, flist, 0));
|
||||
htsmsg_add_msg(l, NULL, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,18 +298,21 @@ api_idnode_load
|
|||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
htsmsg_destroy(flist);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
api_idnode_save
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int err = EINVAL;
|
||||
idnode_t *in;
|
||||
htsmsg_t *msg, *conf;
|
||||
htsmsg_field_t *f;
|
||||
const char *uuid;
|
||||
int count = 0;
|
||||
|
||||
if (!(f = htsmsg_field_find(args, "node")))
|
||||
return EINVAL;
|
||||
|
@ -262,6 +328,10 @@ api_idnode_save
|
|||
goto exit;
|
||||
if (!(in = idnode_find(uuid, NULL)))
|
||||
goto exit;
|
||||
if (idnode_perm(in, perm, msg)) {
|
||||
err = EPERM;
|
||||
goto exit;
|
||||
}
|
||||
idnode_update(in, msg);
|
||||
err = 0;
|
||||
|
||||
|
@ -274,9 +344,15 @@ api_idnode_save
|
|||
continue;
|
||||
if (!(in = idnode_find(uuid, NULL)))
|
||||
continue;
|
||||
if (idnode_perm(in, perm, conf)) {
|
||||
err = EPERM;
|
||||
continue;
|
||||
}
|
||||
count++;
|
||||
idnode_update(in, conf);
|
||||
}
|
||||
err = 0;
|
||||
if (count)
|
||||
err = 0;
|
||||
}
|
||||
|
||||
// TODO: return updated UUIDs?
|
||||
|
@ -289,7 +365,7 @@ exit:
|
|||
|
||||
int
|
||||
api_idnode_tree
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
const char *uuid;
|
||||
const char *root = NULL;
|
||||
|
@ -329,7 +405,7 @@ api_idnode_tree
|
|||
|
||||
/* Children */
|
||||
} else {
|
||||
idnode_set_t *v = node ? idnode_get_childs(node) : rootfn();
|
||||
idnode_set_t *v = node ? idnode_get_childs(node) : rootfn(perm);
|
||||
if (v) {
|
||||
int i;
|
||||
idnode_set_sort_by_title(v);
|
||||
|
@ -348,11 +424,12 @@ api_idnode_tree
|
|||
|
||||
int
|
||||
api_idnode_class
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int err = EINVAL;
|
||||
const char *name;
|
||||
const idclass_t *idc;
|
||||
htsmsg_t *flist = api_idnode_flist_conf(args, "list");
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
|
@ -368,17 +445,20 @@ api_idnode_class
|
|||
}
|
||||
|
||||
err = 0;
|
||||
*resp = idclass_serialize(idc);
|
||||
*resp = idclass_serialize0(idc, flist, 0);
|
||||
|
||||
exit:
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
htsmsg_destroy(flist);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
api_idnode_handler
|
||||
( htsmsg_t *args, htsmsg_t **resp, void (*handler)(idnode_t *in) )
|
||||
( access_t *perm, htsmsg_t *args, htsmsg_t **resp,
|
||||
void (*handler)(access_t *perm, idnode_t *in) )
|
||||
{
|
||||
int err = 0;
|
||||
idnode_t *in;
|
||||
|
@ -400,7 +480,7 @@ api_idnode_handler
|
|||
HTSMSG_FOREACH(f, uuids) {
|
||||
if (!(uuid = htsmsg_field_get_string(f))) continue;
|
||||
if (!(in = idnode_find(uuid, NULL))) continue;
|
||||
handler(in);
|
||||
handler(perm, in);
|
||||
}
|
||||
|
||||
/* Single */
|
||||
|
@ -409,7 +489,7 @@ api_idnode_handler
|
|||
if (!(in = idnode_find(uuid, NULL)))
|
||||
err = ENOENT;
|
||||
else
|
||||
handler(in);
|
||||
handler(perm, in);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
@ -417,25 +497,43 @@ api_idnode_handler
|
|||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
api_idnode_delete_ (access_t *perm, idnode_t *in)
|
||||
{
|
||||
return idnode_delete(in);
|
||||
}
|
||||
|
||||
static int
|
||||
api_idnode_delete
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
return api_idnode_handler(args, resp, idnode_delete);
|
||||
return api_idnode_handler(perm, args, resp, api_idnode_delete_);
|
||||
}
|
||||
|
||||
static void
|
||||
api_idnode_moveup_ (access_t *perm, idnode_t *in)
|
||||
{
|
||||
return idnode_moveup(in);
|
||||
}
|
||||
|
||||
static int
|
||||
api_idnode_moveup
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
return api_idnode_handler(args, resp, idnode_moveup);
|
||||
return api_idnode_handler(perm, args, resp, api_idnode_moveup_);
|
||||
}
|
||||
|
||||
static void
|
||||
api_idnode_movedown_ (access_t *perm, idnode_t *in)
|
||||
{
|
||||
return idnode_movedown(in);
|
||||
}
|
||||
|
||||
static int
|
||||
api_idnode_movedown
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
return api_idnode_handler(args, resp, idnode_movedown);
|
||||
return api_idnode_handler(perm, args, resp, api_idnode_movedown_);
|
||||
}
|
||||
|
||||
void api_idnode_init ( void )
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
static int
|
||||
api_imagecache_load
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
htsmsg_t *l;
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
@ -41,7 +41,7 @@ api_imagecache_load
|
|||
|
||||
static int
|
||||
api_imagecache_save
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
pthread_mutex_lock(&global_lock);
|
||||
if (imagecache_set_config(args))
|
||||
|
|
|
@ -27,28 +27,22 @@
|
|||
|
||||
static int
|
||||
api_intlconv_charset_enum
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
const char **chrst;
|
||||
htsmsg_t *l, *e;
|
||||
|
||||
int _enum = htsmsg_get_bool_or_default(args, "enum", 0);
|
||||
|
||||
if (_enum) {
|
||||
l = htsmsg_create_list();
|
||||
chrst = intlconv_charsets;
|
||||
while (*chrst) {
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_str(e, "key", *chrst);
|
||||
htsmsg_add_str(e, "val", *chrst);
|
||||
htsmsg_add_msg(l, NULL, e);
|
||||
chrst++;
|
||||
}
|
||||
*resp = htsmsg_create_map();
|
||||
htsmsg_add_msg(*resp, "entries", l);
|
||||
} else {
|
||||
// TODO: support full listing v enum
|
||||
l = htsmsg_create_list();
|
||||
chrst = intlconv_charsets;
|
||||
while (*chrst) {
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_str(e, "key", *chrst);
|
||||
htsmsg_add_str(e, "val", *chrst);
|
||||
htsmsg_add_msg(l, NULL, e);
|
||||
chrst++;
|
||||
}
|
||||
*resp = htsmsg_create_map();
|
||||
htsmsg_add_msg(*resp, "entries", l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
*/
|
||||
static int
|
||||
api_mpegts_input_network_list
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int i, err = EINVAL;
|
||||
const char *uuid;
|
||||
|
@ -77,7 +77,7 @@ exit:
|
|||
*/
|
||||
static void
|
||||
api_mpegts_network_grid
|
||||
( idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
mpegts_network_t *mn;
|
||||
|
||||
|
@ -88,7 +88,7 @@ api_mpegts_network_grid
|
|||
|
||||
static int
|
||||
api_mpegts_network_builders
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
mpegts_network_builder_t *mnb;
|
||||
htsmsg_t *l, *e;
|
||||
|
@ -108,7 +108,7 @@ api_mpegts_network_builders
|
|||
|
||||
static int
|
||||
api_mpegts_network_create
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int err;
|
||||
const char *class;
|
||||
|
@ -136,7 +136,7 @@ api_mpegts_network_create
|
|||
|
||||
static int
|
||||
api_mpegts_network_muxclass
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int err = EINVAL;
|
||||
const idclass_t *idc;
|
||||
|
@ -164,7 +164,7 @@ exit:
|
|||
|
||||
static int
|
||||
api_mpegts_network_muxcreate
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int err = EINVAL;
|
||||
mpegts_network_t *mn;
|
||||
|
@ -198,7 +198,7 @@ exit:
|
|||
*/
|
||||
static void
|
||||
api_mpegts_mux_grid
|
||||
( idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
mpegts_network_t *mn;
|
||||
mpegts_mux_t *mm;
|
||||
|
@ -225,7 +225,7 @@ api_mpegts_mux_grid
|
|||
*/
|
||||
static void
|
||||
api_mpegts_service_grid
|
||||
( idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
mpegts_network_t *mn;
|
||||
mpegts_mux_t *mm;
|
||||
|
@ -256,7 +256,7 @@ api_mpegts_service_grid
|
|||
*/
|
||||
static void
|
||||
api_mpegts_mux_sched_grid
|
||||
( idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
mpegts_mux_sched_t *mms;
|
||||
LIST_FOREACH(mms, &mpegts_mux_sched_all, mms_link)
|
||||
|
@ -265,7 +265,7 @@ api_mpegts_mux_sched_grid
|
|||
|
||||
static int
|
||||
api_mpegts_mux_sched_create
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int err;
|
||||
htsmsg_t *conf;
|
||||
|
@ -291,7 +291,7 @@ api_mpegts_mux_sched_create
|
|||
#if ENABLE_MPEGTS_DVB
|
||||
static int
|
||||
api_dvb_scanfile_list
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
char buf[512];
|
||||
const char *type = htsmsg_get_str(args, "type");
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
static int
|
||||
api_mapper_start
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
service_mapper_conf_t conf = { 0 };
|
||||
htsmsg_t *uuids;
|
||||
|
@ -52,7 +52,7 @@ api_mapper_start
|
|||
|
||||
static int
|
||||
api_mapper_stop
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
pthread_mutex_lock(&global_lock);
|
||||
service_mapper_stop();
|
||||
|
@ -78,7 +78,7 @@ api_mapper_status_msg ( void )
|
|||
|
||||
static int
|
||||
api_mapper_status
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
pthread_mutex_lock(&global_lock);
|
||||
*resp = api_mapper_status_msg();
|
||||
|
@ -129,7 +129,7 @@ api_service_streams_get_one ( elementary_stream_t *es, int use_filter )
|
|||
|
||||
static int
|
||||
api_service_streams
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
const char *uuid;
|
||||
htsmsg_t *e, *st, *stf;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
static int
|
||||
api_status_inputs
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int c = 0;
|
||||
htsmsg_t *l, *e;
|
||||
|
@ -59,7 +59,7 @@ api_status_inputs
|
|||
|
||||
static int
|
||||
api_status_subscriptions
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
int c;
|
||||
htsmsg_t *l, *e;
|
||||
|
@ -82,7 +82,7 @@ api_status_subscriptions
|
|||
|
||||
static int
|
||||
api_status_connections
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
pthread_mutex_lock(&global_lock);
|
||||
*resp = tcp_server_connections();
|
||||
|
|
|
@ -163,19 +163,6 @@ channel_class_tags_set ( void *obj, const void *p )
|
|||
return channel_set_tags_by_list(obj, (htsmsg_t*)p);
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
channel_class_tags_enum ( void *obj )
|
||||
{
|
||||
htsmsg_t *e, *m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "type", "api");
|
||||
htsmsg_add_str(m, "uri", "channeltag/list");
|
||||
htsmsg_add_str(m, "event", "channeltag");
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_bool(e, "enum", 1);
|
||||
htsmsg_add_msg(m, "params", e);
|
||||
return m;
|
||||
}
|
||||
|
||||
static void
|
||||
channel_class_icon_notify ( void *obj )
|
||||
{
|
||||
|
@ -221,7 +208,7 @@ channel_class_get_name ( void *p )
|
|||
static const void *
|
||||
channel_class_get_number ( void *p )
|
||||
{
|
||||
static int i;
|
||||
static int64_t i;
|
||||
i = channel_get_number(p);
|
||||
return &i;
|
||||
}
|
||||
|
@ -288,6 +275,7 @@ channel_class_epggrab_list ( void *o )
|
|||
const idclass_t channel_class = {
|
||||
.ic_class = "channel",
|
||||
.ic_caption = "Channel",
|
||||
.ic_event = "channel",
|
||||
.ic_save = channel_class_save,
|
||||
.ic_get_title = channel_class_get_title,
|
||||
.ic_delete = channel_class_delete,
|
||||
|
@ -308,7 +296,8 @@ const idclass_t channel_class = {
|
|||
.get = channel_class_get_name,
|
||||
},
|
||||
{
|
||||
.type = PT_INT,
|
||||
.type = PT_S64,
|
||||
.intsplit = CHANNEL_SPLIT,
|
||||
.id = "number",
|
||||
.name = "Number",
|
||||
.off = offsetof(channel_t, ch_number),
|
||||
|
@ -369,7 +358,7 @@ const idclass_t channel_class = {
|
|||
.name = "Tags",
|
||||
.get = channel_class_tags_get,
|
||||
.set = channel_class_tags_set,
|
||||
.list = channel_class_tags_enum,
|
||||
.list = channel_tag_class_get_list,
|
||||
.rend = channel_class_tags_rend
|
||||
},
|
||||
{}
|
||||
|
@ -430,7 +419,8 @@ channel_access(channel_t *ch, access_t *a, const char *username)
|
|||
htsmsg_field_t *f;
|
||||
HTSMSG_FOREACH(f, a->aa_chtags) {
|
||||
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
|
||||
if (!strcmp(htsmsg_field_get_str(f) ?: "", ctm->ctm_tag->ct_name))
|
||||
if (!strcmp(htsmsg_field_get_str(f) ?: "",
|
||||
idnode_uuid_as_str(&ctm->ctm_tag->ct_id)))
|
||||
goto chtags_ok;
|
||||
}
|
||||
}
|
||||
|
@ -438,17 +428,6 @@ channel_access(channel_t *ch, access_t *a, const char *username)
|
|||
}
|
||||
chtags_ok:
|
||||
|
||||
/* Channel tag <-> user name match */
|
||||
if (ch && (a->aa_rights & ACCESS_TAG_ONLY) != 0) {
|
||||
channel_tag_mapping_t *ctm;
|
||||
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
|
||||
if (!strcmp(username ?: "", ctm->ctm_tag->ct_name))
|
||||
goto tagonly_ok;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
tagonly_ok:
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -529,7 +508,7 @@ channel_get_name ( channel_t *ch )
|
|||
return blank;
|
||||
}
|
||||
|
||||
int
|
||||
int64_t
|
||||
channel_get_number ( channel_t *ch )
|
||||
{
|
||||
int n;
|
||||
|
@ -620,7 +599,7 @@ channel_delete ( channel_t *ch, int delconf )
|
|||
|
||||
/* Settings */
|
||||
if (delconf)
|
||||
hts_settings_remove("channel/%s", idnode_uuid_as_str(&ch->ch_id));
|
||||
hts_settings_remove("channel/config/%s", idnode_uuid_as_str(&ch->ch_id));
|
||||
|
||||
/* Free memory */
|
||||
RB_REMOVE(&channels, ch, ch_link);
|
||||
|
@ -638,7 +617,7 @@ channel_save ( channel_t *ch )
|
|||
{
|
||||
htsmsg_t *c = htsmsg_create_map();
|
||||
idnode_save(&ch->ch_id, c);
|
||||
hts_settings_save(c, "channel/%s", idnode_uuid_as_str(&ch->ch_id));
|
||||
hts_settings_save(c, "channel/config/%s", idnode_uuid_as_str(&ch->ch_id));
|
||||
htsmsg_destroy(c);
|
||||
}
|
||||
|
||||
|
@ -656,7 +635,7 @@ channel_init ( void )
|
|||
channel_tag_init();
|
||||
|
||||
/* Channels */
|
||||
if (!(c = hts_settings_load_r(1, "channel")))
|
||||
if (!(c = hts_settings_load("channel/config")))
|
||||
return;
|
||||
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
|
@ -761,6 +740,9 @@ channel_tag_create(const char *uuid, htsmsg_t *conf)
|
|||
channel_tag_t *ct;
|
||||
|
||||
ct = calloc(1, sizeof(channel_tag_t));
|
||||
LIST_INIT(&ct->ct_ctms);
|
||||
LIST_INIT(&ct->ct_autorecs);
|
||||
LIST_INIT(&ct->ct_accesses);
|
||||
|
||||
if (idnode_insert(&ct->ct_id, uuid, &channel_tag_class, IDNODE_SHORT_UUID)) {
|
||||
if (uuid)
|
||||
|
@ -798,7 +780,7 @@ channel_tag_destroy(channel_tag_t *ct, int delconf)
|
|||
channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_CHANNEL);
|
||||
channel_save(ch);
|
||||
}
|
||||
hts_settings_remove("channeltags/%s", idnode_uuid_as_str(&ct->ct_id));
|
||||
hts_settings_remove("channel/tag/%s", idnode_uuid_as_str(&ct->ct_id));
|
||||
}
|
||||
|
||||
if(ct->ct_enabled && !ct->ct_internal)
|
||||
|
@ -807,6 +789,9 @@ channel_tag_destroy(channel_tag_t *ct, int delconf)
|
|||
TAILQ_REMOVE(&channel_tags, ct, ct_link);
|
||||
idnode_unlink(&ct->ct_id);
|
||||
|
||||
autorec_destroy_by_channel_tag(ct, delconf);
|
||||
access_destroy_by_channel_tag(ct, delconf);
|
||||
|
||||
free(ct->ct_name);
|
||||
free(ct->ct_comment);
|
||||
free(ct->ct_icon);
|
||||
|
@ -821,7 +806,7 @@ channel_tag_save(channel_tag_t *ct)
|
|||
{
|
||||
htsmsg_t *c = htsmsg_create_map();
|
||||
idnode_save(&ct->ct_id, c);
|
||||
hts_settings_save(c, "channeltags/%s", idnode_uuid_as_str(&ct->ct_id));
|
||||
hts_settings_save(c, "channel/tag/%s", idnode_uuid_as_str(&ct->ct_id));
|
||||
htsmsg_destroy(c);
|
||||
}
|
||||
|
||||
|
@ -849,9 +834,21 @@ channel_tag_class_get_title (idnode_t *self)
|
|||
return ct->ct_name ?: "";
|
||||
}
|
||||
|
||||
/* exported for others */
|
||||
htsmsg_t *
|
||||
channel_tag_class_get_list(void *o)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "type", "api");
|
||||
htsmsg_add_str(m, "uri", "channeltag/list");
|
||||
htsmsg_add_str(m, "event", "channeltag");
|
||||
return m;
|
||||
}
|
||||
|
||||
const idclass_t channel_tag_class = {
|
||||
.ic_class = "channeltag",
|
||||
.ic_caption = "Channel Tag",
|
||||
.ic_event = "channeltag",
|
||||
.ic_save = channel_tag_class_save,
|
||||
.ic_get_title = channel_tag_class_get_title,
|
||||
.ic_delete = channel_tag_class_delete,
|
||||
|
@ -946,7 +943,7 @@ channel_tag_init ( void )
|
|||
htsmsg_field_t *f;
|
||||
|
||||
TAILQ_INIT(&channel_tags);
|
||||
if ((c = hts_settings_load_r(1, "channeltags")) != NULL) {
|
||||
if ((c = hts_settings_load("channel/tag")) != NULL) {
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(m = htsmsg_field_get_map(f))) continue;
|
||||
(void)channel_tag_create(f->hmf_name, m);
|
||||
|
|
|
@ -47,10 +47,10 @@ typedef struct channel
|
|||
int ch_zombie;
|
||||
|
||||
/* Channel info */
|
||||
char *ch_name; // Note: do not access directly!
|
||||
int ch_number;
|
||||
char *ch_icon;
|
||||
struct channel_tag_mapping_list ch_ctms;
|
||||
char *ch_name; // Note: do not access directly!
|
||||
int64_t ch_number;
|
||||
char *ch_icon;
|
||||
struct channel_tag_mapping_list ch_ctms;
|
||||
|
||||
/* Service/subscriptions */
|
||||
LIST_HEAD(, channel_service_mapping) ch_services;
|
||||
|
@ -95,6 +95,8 @@ typedef struct channel_tag {
|
|||
|
||||
struct dvr_autorec_entry_list ct_autorecs;
|
||||
|
||||
struct access_entry_list ct_accesses;
|
||||
|
||||
int ct_htsp_id;
|
||||
|
||||
} channel_tag_t;
|
||||
|
@ -164,6 +166,8 @@ static inline channel_tag_t *channel_tag_find_by_uuid(const char *uuid)
|
|||
|
||||
void channel_tag_save(channel_tag_t *ct);
|
||||
|
||||
htsmsg_t * channel_tag_class_get_list(void *o);
|
||||
|
||||
int channel_access(channel_t *ch, struct access *a, const char *username);
|
||||
|
||||
int channel_tag_map(channel_t *ch, channel_tag_t *ct);
|
||||
|
@ -173,7 +177,12 @@ void channel_save(channel_t *ch);
|
|||
const char *channel_get_name ( channel_t *ch );
|
||||
int channel_set_name ( channel_t *ch, const char *s );
|
||||
|
||||
int channel_get_number ( channel_t *ch );
|
||||
#define CHANNEL_SPLIT 1000000
|
||||
|
||||
static inline uint32_t channel_get_major ( int64_t chnum ) { return chnum / CHANNEL_SPLIT; }
|
||||
static inline uint32_t channel_get_minor ( int64_t chnum ) { return chnum % CHANNEL_SPLIT; }
|
||||
|
||||
int64_t channel_get_number ( channel_t *ch );
|
||||
|
||||
const char *channel_get_icon ( channel_t *ch );
|
||||
int channel_set_icon ( channel_t *ch, const char *icon );
|
||||
|
|
404
src/config.c
404
src/config.c
|
@ -16,13 +16,15 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "tvheadend.h"
|
||||
#include "settings.h"
|
||||
#include "config.h"
|
||||
#include "uuid.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include "htsbuf.h"
|
||||
#include "spawn.h"
|
||||
|
||||
/* *************************************************************************
|
||||
* Global data
|
||||
|
@ -620,7 +622,7 @@ config_migrate_v6 ( void )
|
|||
* v6 -> v7 : acesscontrol changes
|
||||
*/
|
||||
static void
|
||||
config_migrate_simple ( const char *dir, htsmsg_t **orig,
|
||||
config_migrate_simple ( const char *dir, htsmsg_t *list,
|
||||
void (*modify)(htsmsg_t *record,
|
||||
uint32_t id,
|
||||
const char *uuid,
|
||||
|
@ -632,25 +634,31 @@ config_migrate_simple ( const char *dir, htsmsg_t **orig,
|
|||
tvh_uuid_t u;
|
||||
uint32_t index = 1, id;
|
||||
|
||||
if (!(c = hts_settings_load_r(1, dir)))
|
||||
if (!(c = hts_settings_load(dir)))
|
||||
return;
|
||||
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
uuid_init_hex(&u, NULL);
|
||||
if (htsmsg_get_u32(e, "id", &id))
|
||||
id = 0;
|
||||
else if (list) {
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%d", id);
|
||||
htsmsg_add_str(m, "id", buf);
|
||||
htsmsg_add_str(m, "uuid", u.hex);
|
||||
htsmsg_add_msg(list, NULL, m);
|
||||
}
|
||||
htsmsg_delete_field(e, "id");
|
||||
htsmsg_add_u32(e, "index", index++);
|
||||
uuid_init_hex(&u, NULL);
|
||||
modify(e, id, u.hex, aux);
|
||||
if (modify)
|
||||
modify(e, id, u.hex, aux);
|
||||
hts_settings_save(e, "%s/%s", dir, u.hex);
|
||||
hts_settings_remove("%s/%s", dir, f->hmf_name);
|
||||
}
|
||||
|
||||
if (orig)
|
||||
*orig = c;
|
||||
else
|
||||
htsmsg_destroy(c);
|
||||
htsmsg_destroy(c);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -731,6 +739,313 @@ config_migrate_v8 ( void )
|
|||
htsmsg_destroy(ch);
|
||||
}
|
||||
|
||||
static void
|
||||
config_modify_autorec( htsmsg_t *c, uint32_t id, const char *uuid, void *aux )
|
||||
{
|
||||
uint32_t u32;
|
||||
htsmsg_delete_field(c, "index");
|
||||
if (!htsmsg_get_u32(c, "approx_time", &u32)) {
|
||||
htsmsg_delete_field(c, "approx_time");
|
||||
if (u32 != 0)
|
||||
htsmsg_add_u32(c, "start", u32);
|
||||
else
|
||||
htsmsg_add_str(c, "start", "");
|
||||
}
|
||||
if (!htsmsg_get_u32(c, "contenttype", &u32)) {
|
||||
htsmsg_delete_field(c, "contenttype");
|
||||
htsmsg_add_u32(c, "content_type", u32 / 16);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config_modify_dvr_log( htsmsg_t *c, uint32_t id, const char *uuid, void *aux )
|
||||
{
|
||||
htsmsg_t *list = aux;
|
||||
const char *chname = htsmsg_get_str(c, "channelname");
|
||||
const char *chuuid = htsmsg_get_str(c, "channel");
|
||||
htsmsg_t *e;
|
||||
htsmsg_field_t *f;
|
||||
tvh_uuid_t uuid0;
|
||||
const char *s1;
|
||||
uint32_t u32;
|
||||
|
||||
htsmsg_delete_field(c, "index");
|
||||
if (chname == NULL || (chuuid != NULL && uuid_init_bin(&uuid0, chuuid))) {
|
||||
chname = strdup(chuuid);
|
||||
htsmsg_delete_field(c, "channelname");
|
||||
htsmsg_delete_field(c, "channel");
|
||||
htsmsg_add_str(c, "channelname", chname);
|
||||
free((char *)chname);
|
||||
if (!htsmsg_get_u32(c, "contenttype", &u32)) {
|
||||
htsmsg_delete_field(c, "contenttype");
|
||||
htsmsg_add_u32(c, "content_type", u32 / 16);
|
||||
}
|
||||
}
|
||||
if ((s1 = htsmsg_get_str(c, "autorec")) != NULL) {
|
||||
s1 = strdup(s1);
|
||||
htsmsg_delete_field(c, "autorec");
|
||||
HTSMSG_FOREACH(f, list) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
if (strcmp(s1, htsmsg_get_str(e, "id")) == 0) {
|
||||
htsmsg_add_str(c, "autorec", htsmsg_get_str(e, "uuid"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config_migrate_v9 ( void )
|
||||
{
|
||||
htsmsg_t *list = htsmsg_create_list();
|
||||
htsmsg_t *c, *e;
|
||||
htsmsg_field_t *f;
|
||||
tvh_uuid_t u;
|
||||
|
||||
config_migrate_simple("autorec", list, config_modify_autorec, NULL);
|
||||
config_migrate_simple("dvr/log", NULL, config_modify_dvr_log, list);
|
||||
htsmsg_destroy(list);
|
||||
|
||||
if ((c = hts_settings_load("dvr")) != NULL) {
|
||||
/* step 1: only "config" */
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
if (strcmp(f->hmf_name, "config")) continue;
|
||||
htsmsg_add_str(e, "name", f->hmf_name + 6);
|
||||
uuid_init_hex(&u, NULL);
|
||||
hts_settings_remove("dvr/%s", f->hmf_name);
|
||||
hts_settings_save(e, "dvr/config/%s", u.hex);
|
||||
}
|
||||
/* step 2: reset (without "config") */
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
if (strcmp(f->hmf_name, "config") == 0) continue;
|
||||
if (strncmp(f->hmf_name, "config", 6)) continue;
|
||||
htsmsg_add_str(e, "name", f->hmf_name + 6);
|
||||
uuid_init_hex(&u, NULL);
|
||||
hts_settings_remove("dvr/%s", f->hmf_name);
|
||||
hts_settings_save(e, "dvr/config/%s", u.hex);
|
||||
}
|
||||
htsmsg_destroy(c);
|
||||
}
|
||||
|
||||
if ((c = hts_settings_load("autorec")) != NULL) {
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
hts_settings_remove("autorec/%s", f->hmf_name);
|
||||
hts_settings_save(e, "dvr/autorec/%s", f->hmf_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config_migrate_move ( const char *dir,
|
||||
const char *newdir )
|
||||
{
|
||||
htsmsg_t *c, *e;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
if (!(c = hts_settings_load(dir)))
|
||||
return;
|
||||
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
hts_settings_save(e, "%s/%s", newdir, f->hmf_name);
|
||||
hts_settings_remove("%s/%s", dir, f->hmf_name);
|
||||
}
|
||||
|
||||
htsmsg_destroy(c);
|
||||
}
|
||||
|
||||
static void
|
||||
config_migrate_v10 ( void )
|
||||
{
|
||||
config_migrate_move("channel", "channel/config");
|
||||
config_migrate_move("channeltags", "channel/tag");
|
||||
}
|
||||
|
||||
static const char *
|
||||
config_find_uuid( htsmsg_t *map, const char *name, const char *value )
|
||||
{
|
||||
htsmsg_t *e;
|
||||
htsmsg_field_t *f;
|
||||
const char *s;
|
||||
|
||||
HTSMSG_FOREACH(f, map) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
if ((s = htsmsg_get_str(e, name)) != NULL) {
|
||||
if (!strcmp(s, value))
|
||||
return f->hmf_name;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
config_modify_acl_dvallcfg( htsmsg_t *c, htsmsg_t *dvr_config )
|
||||
{
|
||||
uint32_t a;
|
||||
const char *username, *uuid;
|
||||
|
||||
username = htsmsg_get_str(c, "username");
|
||||
if (!htsmsg_get_u32(c, "dvallcfg", &a))
|
||||
if (a == 0) {
|
||||
uuid = username ? config_find_uuid(dvr_config, "name", username) : NULL;
|
||||
if (uuid)
|
||||
htsmsg_add_str(c, "dvr_config", uuid);
|
||||
}
|
||||
htsmsg_delete_field(c, "dvallcfg");
|
||||
}
|
||||
|
||||
static void
|
||||
config_modify_acl_tag_only( htsmsg_t *c, htsmsg_t *channel_tag )
|
||||
{
|
||||
uint32_t a;
|
||||
const char *username, *tag, *uuid;
|
||||
|
||||
username = htsmsg_get_str(c, "username");
|
||||
tag = htsmsg_get_str(c, "channel_tag");
|
||||
if (!tag || tag[0] == '\0')
|
||||
tag = NULL;
|
||||
if (tag == NULL && !htsmsg_get_u32(c, "tag_only", &a)) {
|
||||
if (a) {
|
||||
uuid = username ? config_find_uuid(channel_tag, "name", username) : NULL;
|
||||
if (uuid)
|
||||
htsmsg_add_str(c, "channel_tag", uuid);
|
||||
}
|
||||
} else if (tag) {
|
||||
uuid = config_find_uuid(channel_tag, "name", tag);
|
||||
if (uuid) {
|
||||
htsmsg_delete_field(c, "channel_tag");
|
||||
htsmsg_add_str(c, "channel_tag", uuid);
|
||||
}
|
||||
}
|
||||
htsmsg_delete_field(c, "tag_only");
|
||||
}
|
||||
|
||||
static void
|
||||
config_modify_dvr_config_name( htsmsg_t *c, htsmsg_t *dvr_config )
|
||||
{
|
||||
const char *config_name, *uuid;
|
||||
|
||||
config_name = htsmsg_get_str(c, "config_name");
|
||||
uuid = config_name ? config_find_uuid(dvr_config, "name", config_name) : NULL;
|
||||
htsmsg_delete_field(c, "config_name");
|
||||
htsmsg_add_str(c, "config_name", uuid ?: "");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
config_migrate_v11 ( void )
|
||||
{
|
||||
htsmsg_t *dvr_config;
|
||||
htsmsg_t *channel_tag;
|
||||
htsmsg_t *c, *e;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
dvr_config = hts_settings_load("dvr/config");
|
||||
channel_tag = hts_settings_load("channel/tag");
|
||||
|
||||
if ((c = hts_settings_load("accesscontrol")) != NULL) {
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
config_modify_acl_dvallcfg(e, dvr_config);
|
||||
config_modify_acl_tag_only(e, channel_tag);
|
||||
}
|
||||
htsmsg_destroy(c);
|
||||
}
|
||||
|
||||
if ((c = hts_settings_load("dvr/log")) != NULL) {
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
config_modify_dvr_config_name(e, dvr_config);
|
||||
}
|
||||
htsmsg_destroy(c);
|
||||
}
|
||||
|
||||
htsmsg_destroy(channel_tag);
|
||||
htsmsg_destroy(dvr_config);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform backup
|
||||
*/
|
||||
static void
|
||||
dobackup(const char *oldver)
|
||||
{
|
||||
char outfile[PATH_MAX], cwd[PATH_MAX];
|
||||
const char *argv[] = {
|
||||
"/usr/bin/tar", "cjf", outfile, "--exclude", "backup", ".", NULL
|
||||
};
|
||||
const char *root = hts_settings_get_root();
|
||||
char errtxt[128];
|
||||
const char **arg;
|
||||
int code;
|
||||
|
||||
tvhinfo("config", "backup: migrating config from %s (running %s)",
|
||||
oldver, tvheadend_version);
|
||||
|
||||
if (getcwd(cwd, sizeof(cwd)) == NULL) {
|
||||
tvherror("config", "unable to get the current working directory");
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
if (!access("/bin/tar", X_OK))
|
||||
argv[0] = "/bin/tar";
|
||||
else if (!access("/usr/bin/tar", X_OK))
|
||||
argv[0] = "/usr/bin/tar";
|
||||
else if (!access("/usr/local/bin/tar", X_OK))
|
||||
argv[0] = "/usr/local/bin/tar";
|
||||
else {
|
||||
tvherror("config", "unable to find tar program");
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
snprintf(outfile, sizeof(outfile), "%s/backup", root);
|
||||
if (makedirs(outfile, 0700))
|
||||
goto fatal;
|
||||
if (chdir(root)) {
|
||||
tvherror("config", "unable to find directory '%s'", root);
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
snprintf(outfile, sizeof(outfile), "%s/backup/%s.tar.bz2",
|
||||
root, oldver);
|
||||
tvhinfo("config", "backup: running, output file %s", outfile);
|
||||
|
||||
spawnv(argv[0], (void *)argv);
|
||||
|
||||
while ((code = spawn_reap(errtxt, sizeof(errtxt))) == -EAGAIN)
|
||||
usleep(20000);
|
||||
|
||||
if (code) {
|
||||
htsbuf_queue_t q;
|
||||
char *s;
|
||||
htsbuf_queue_init(&q, 0);
|
||||
for (arg = argv; *arg; arg++) {
|
||||
htsbuf_append(&q, *arg, strlen(*arg));
|
||||
if (arg[1])
|
||||
htsbuf_append(&q, " ", 1);
|
||||
}
|
||||
s = htsbuf_to_string(&q);
|
||||
tvherror("config", "command '%s' returned error code %d", s, code);
|
||||
tvherror("config", "executed in directory '%s'", root);
|
||||
free(s);
|
||||
htsbuf_queue_flush(&q);
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
if (chdir(cwd)) {
|
||||
tvherror("config", "unable to change directory to '%s'", cwd);
|
||||
goto fatal;
|
||||
}
|
||||
return;
|
||||
|
||||
fatal:
|
||||
tvherror("config", "backup: fatal error");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Migration table
|
||||
*/
|
||||
|
@ -743,18 +1058,28 @@ static const config_migrate_t config_migrate_table[] = {
|
|||
config_migrate_v6,
|
||||
config_migrate_v7,
|
||||
config_migrate_v8,
|
||||
config_migrate_v9,
|
||||
config_migrate_v10,
|
||||
config_migrate_v11
|
||||
};
|
||||
|
||||
/*
|
||||
* Perform migrations (if required)
|
||||
*/
|
||||
static void
|
||||
config_migrate ( void )
|
||||
static int
|
||||
config_migrate ( int backup )
|
||||
{
|
||||
uint32_t v;
|
||||
const char *s;
|
||||
|
||||
/* Get the current version */
|
||||
v = htsmsg_get_u32_or_default(config, "version", 0);
|
||||
s = htsmsg_get_str(config, "fullversion") ?: "unknown";
|
||||
|
||||
if (backup && strcmp(s, tvheadend_version))
|
||||
dobackup(s);
|
||||
else
|
||||
backup = 0;
|
||||
|
||||
/* Attempt to auto-detect versions prior to v2 */
|
||||
if (!v) {
|
||||
|
@ -765,8 +1090,11 @@ config_migrate ( void )
|
|||
}
|
||||
|
||||
/* No changes required */
|
||||
if (v == ARRAY_SIZE(config_migrate_table))
|
||||
return;
|
||||
if (v == ARRAY_SIZE(config_migrate_table)) {
|
||||
if (backup)
|
||||
goto update;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Run migrations */
|
||||
for ( ; v < ARRAY_SIZE(config_migrate_table); v++) {
|
||||
|
@ -775,8 +1103,48 @@ config_migrate ( void )
|
|||
}
|
||||
|
||||
/* Update */
|
||||
update:
|
||||
htsmsg_set_u32(config, "version", v);
|
||||
htsmsg_set_str(config, "fullversion", tvheadend_version);
|
||||
config_save();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void
|
||||
config_check_one ( const char *dir )
|
||||
{
|
||||
htsmsg_t *c, *e;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
if (!(c = hts_settings_load(dir)))
|
||||
return;
|
||||
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(e = htsmsg_field_get_map(f))) continue;
|
||||
if (strlen(f->hmf_name) != UUID_HEX_SIZE - 1) {
|
||||
tvherror("START", "filename %s/%s/%s is invalid", hts_settings_get_root(), dir, f->hmf_name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
htsmsg_destroy(c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform a simple check for UUID files
|
||||
*/
|
||||
static void
|
||||
config_check ( void )
|
||||
{
|
||||
config_check_one("accesscontrol");
|
||||
config_check_one("channel/config");
|
||||
config_check_one("channel/tag");
|
||||
config_check_one("dvr/config");
|
||||
config_check_one("dvr/log");
|
||||
config_check_one("dvr/autorec");
|
||||
config_check_one("esfilter");
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
|
@ -784,7 +1152,7 @@ config_migrate ( void )
|
|||
* *************************************************************************/
|
||||
|
||||
void
|
||||
config_init ( const char *path )
|
||||
config_init ( const char *path, int backup )
|
||||
{
|
||||
struct stat st;
|
||||
char buf[1024];
|
||||
|
@ -829,11 +1197,13 @@ config_init ( const char *path )
|
|||
/* Store version number */
|
||||
if (new) {
|
||||
htsmsg_set_u32(config, "version", ARRAY_SIZE(config_migrate_table));
|
||||
htsmsg_set_str(config, "fullversion", tvheadend_version);
|
||||
config_save();
|
||||
|
||||
/* Perform migrations */
|
||||
} else {
|
||||
config_migrate();
|
||||
if (config_migrate(backup))
|
||||
config_check();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "htsmsg.h"
|
||||
|
||||
void config_init ( const char *path );
|
||||
void config_init ( const char *path, int backup );
|
||||
void config_done ( void );
|
||||
void config_save ( void );
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ cron_parse_field
|
|||
if ((sn - off) >= bits || (en - off) >= bits || mn > bits)
|
||||
return 1;
|
||||
if (en < 0) en = sn;
|
||||
if (mn < 0) mn = 1;
|
||||
if (mn <= 0) mn = 1;
|
||||
while (sn <= en) {
|
||||
if ( (sn % mn) == 0 )
|
||||
val |= (0x1ULL << (sn - off));
|
||||
|
@ -176,7 +176,7 @@ cron_multi_set ( const char *str )
|
|||
if (line[0] != '#')
|
||||
if (!cron_set(&cron, line)) {
|
||||
count++;
|
||||
cm2 = realloc(cm, sizeof(cm) + sizeof(cron) * count);
|
||||
cm2 = realloc(cm, sizeof(*cm) + sizeof(cron) * count);
|
||||
if (cm2 == NULL) {
|
||||
free(cm);
|
||||
return NULL;
|
||||
|
|
215
src/dvr/dvr.h
215
src/dvr/dvr.h
|
@ -27,18 +27,39 @@
|
|||
#include "lang_str.h"
|
||||
|
||||
typedef struct dvr_config {
|
||||
idnode_t dvr_id;
|
||||
|
||||
LIST_ENTRY(dvr_config) config_link;
|
||||
|
||||
int dvr_enabled;
|
||||
int dvr_valid;
|
||||
char *dvr_config_name;
|
||||
char *dvr_storage;
|
||||
uint32_t dvr_retention_days;
|
||||
int dvr_flags;
|
||||
char *dvr_charset;
|
||||
char *dvr_charset_id;
|
||||
char *dvr_postproc;
|
||||
int dvr_extra_time_pre;
|
||||
int dvr_extra_time_post;
|
||||
uint32_t dvr_extra_time_pre;
|
||||
uint32_t dvr_extra_time_post;
|
||||
|
||||
muxer_container_type_t dvr_mc;
|
||||
muxer_config_t dvr_muxcnf;
|
||||
int dvr_mc;
|
||||
muxer_config_t dvr_muxcnf;
|
||||
|
||||
int dvr_dir_per_day;
|
||||
int dvr_channel_dir;
|
||||
int dvr_channel_in_title;
|
||||
int dvr_omit_title;
|
||||
int dvr_date_in_title;
|
||||
int dvr_time_in_title;
|
||||
int dvr_whitespace_in_title;
|
||||
int dvr_title_dir;
|
||||
int dvr_episode_in_title;
|
||||
int dvr_clean_title;
|
||||
int dvr_tag_files;
|
||||
int dvr_skip_commercials;
|
||||
int dvr_subtitle_in_title;
|
||||
int dvr_episode_before_date;
|
||||
int dvr_episode_duplicate;
|
||||
|
||||
/* Series link support */
|
||||
int dvr_sl_brand_lock;
|
||||
|
@ -51,28 +72,16 @@ typedef struct dvr_config {
|
|||
/* Duplicate detect */
|
||||
int dvr_dup_detect_episode;
|
||||
|
||||
LIST_ENTRY(dvr_config) config_link;
|
||||
struct dvr_entry_list dvr_entries;
|
||||
|
||||
struct access_entry_list dvr_accesses;
|
||||
|
||||
} dvr_config_t;
|
||||
|
||||
extern struct dvr_config_list dvrconfigs;
|
||||
|
||||
extern struct dvr_entry_list dvrentries;
|
||||
|
||||
#define DVR_DIR_PER_DAY 0x1
|
||||
#define DVR_DIR_PER_CHANNEL 0x2
|
||||
#define DVR_CHANNEL_IN_TITLE 0x4
|
||||
#define DVR_DATE_IN_TITLE 0x8
|
||||
#define DVR_TIME_IN_TITLE 0x10
|
||||
#define DVR_WHITESPACE_IN_TITLE 0x20
|
||||
#define DVR_DIR_PER_TITLE 0x40
|
||||
#define DVR_EPISODE_IN_TITLE 0x80
|
||||
#define DVR_CLEAN_TITLE 0x100
|
||||
#define DVR_TAG_FILES 0x200
|
||||
#define DVR_SKIP_COMMERCIALS 0x400
|
||||
#define DVR_SUBTITLE_IN_TITLE 0x800
|
||||
#define DVR_EPISODE_BEFORE_DATE 0x1000
|
||||
#define DVR_EPISODE_DUPLICATE_DETECTION 0x2000
|
||||
|
||||
typedef enum {
|
||||
DVR_PRIO_IMPORTANT,
|
||||
DVR_PRIO_HIGH,
|
||||
|
@ -106,6 +115,8 @@ typedef enum {
|
|||
|
||||
typedef struct dvr_entry {
|
||||
|
||||
idnode_t de_id;
|
||||
|
||||
int de_refcnt; /* Modification is protected under global_lock */
|
||||
|
||||
|
||||
|
@ -115,7 +126,6 @@ typedef struct dvr_entry {
|
|||
*/
|
||||
|
||||
LIST_ENTRY(dvr_entry) de_global_link;
|
||||
int de_id;
|
||||
|
||||
channel_t *de_channel;
|
||||
LIST_ENTRY(dvr_entry) de_channel_link;
|
||||
|
@ -128,7 +138,8 @@ typedef struct dvr_entry {
|
|||
* These meta fields will stay valid as long as reference count > 0
|
||||
*/
|
||||
|
||||
char *de_config_name;
|
||||
dvr_config_t *de_config;
|
||||
LIST_ENTRY(dvr_entry) de_config_link;
|
||||
|
||||
time_t de_start;
|
||||
time_t de_stop;
|
||||
|
@ -141,15 +152,13 @@ typedef struct dvr_entry {
|
|||
generated yet */
|
||||
lang_str_t *de_title; /* Title in UTF-8 (from EPG) */
|
||||
lang_str_t *de_desc; /* Description in UTF-8 (from EPG) */
|
||||
epg_genre_t de_content_type; /* Content type (from EPG) */
|
||||
uint32_t de_content_type; /* Content type (from EPG) (only code) */
|
||||
|
||||
uint16_t de_dvb_eid;
|
||||
|
||||
dvr_prio_t de_pri;
|
||||
|
||||
uint32_t de_dont_reschedule;
|
||||
|
||||
muxer_container_type_t de_mc;
|
||||
int de_pri;
|
||||
int de_dont_reschedule;
|
||||
int de_mc;
|
||||
|
||||
/**
|
||||
* EPG information / links
|
||||
|
@ -214,9 +223,11 @@ typedef struct dvr_entry {
|
|||
* Autorec entry
|
||||
*/
|
||||
typedef struct dvr_autorec_entry {
|
||||
TAILQ_ENTRY(dvr_autorec_entry) dae_link;
|
||||
char *dae_id;
|
||||
idnode_t dae_id;
|
||||
|
||||
TAILQ_ENTRY(dvr_autorec_entry) dae_link;
|
||||
|
||||
char *dae_name;
|
||||
char *dae_config_name;
|
||||
|
||||
int dae_enabled;
|
||||
|
@ -226,11 +237,11 @@ typedef struct dvr_autorec_entry {
|
|||
char *dae_title;
|
||||
regex_t dae_title_preg;
|
||||
|
||||
epg_genre_t dae_content_type;
|
||||
uint32_t dae_content_type;
|
||||
|
||||
int dae_approx_time; /* Minutes from midnight */
|
||||
int dae_start; /* Minutes from midnight */
|
||||
|
||||
int dae_weekdays;
|
||||
uint32_t dae_weekdays;
|
||||
|
||||
channel_t *dae_channel;
|
||||
LIST_ENTRY(dvr_autorec_entry) dae_channel_link;
|
||||
|
@ -251,6 +262,17 @@ typedef struct dvr_autorec_entry {
|
|||
int dae_maxduration;
|
||||
} dvr_autorec_entry_t;
|
||||
|
||||
TAILQ_HEAD(dvr_autorec_entry_queue, dvr_autorec_entry);
|
||||
|
||||
extern struct dvr_autorec_entry_queue autorec_entries;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
extern const idclass_t dvr_config_class;
|
||||
extern const idclass_t dvr_entry_class;
|
||||
extern const idclass_t dvr_autorec_entry_class;
|
||||
|
||||
/**
|
||||
* Prototypes
|
||||
|
@ -258,15 +280,32 @@ typedef struct dvr_autorec_entry {
|
|||
|
||||
void dvr_make_title(char *output, size_t outlen, dvr_entry_t *de);
|
||||
|
||||
static inline int dvr_config_is_valid(dvr_config_t *cfg)
|
||||
{ return cfg->dvr_valid; }
|
||||
|
||||
static inline int dvr_config_is_default(dvr_config_t *cfg)
|
||||
{ return cfg->dvr_config_name == NULL || cfg->dvr_config_name[0] == '\0'; }
|
||||
|
||||
dvr_config_t *dvr_config_find_by_name(const char *name);
|
||||
|
||||
dvr_config_t *dvr_config_find_by_name_default(const char *name);
|
||||
|
||||
dvr_config_t *dvr_config_create(const char *name);
|
||||
dvr_config_t *dvr_config_create(const char *name, const char *uuid, htsmsg_t *conf);
|
||||
|
||||
static inline dvr_config_t *dvr_config_find_by_uuid(const char *uuid)
|
||||
{ return (dvr_config_t*)idnode_find(uuid, &dvr_config_class); }
|
||||
|
||||
void dvr_config_delete(const char *name);
|
||||
|
||||
void dvr_entry_notify(dvr_entry_t *de);
|
||||
void dvr_config_save(dvr_config_t *cfg);
|
||||
|
||||
static inline int dvr_entry_is_editable(dvr_entry_t *de)
|
||||
{ return de->de_sched_state == DVR_SCHEDULED; }
|
||||
|
||||
static inline int dvr_entry_is_valid(dvr_entry_t *de)
|
||||
{ return de->de_refcnt > 0; }
|
||||
|
||||
int dvr_entry_get_mc(dvr_entry_t *de);
|
||||
|
||||
void dvr_entry_save(dvr_entry_t *de);
|
||||
|
||||
|
@ -276,30 +315,37 @@ const char *dvr_entry_schedstatus(dvr_entry_t *de);
|
|||
|
||||
void dvr_entry_create_by_autorec(epg_broadcast_t *e, dvr_autorec_entry_t *dae);
|
||||
|
||||
dvr_entry_t *dvr_entry_create_by_event
|
||||
(const char *dvr_config_name,
|
||||
epg_broadcast_t *e,
|
||||
time_t start_extra, time_t stop_extra,
|
||||
const char *creator,
|
||||
dvr_autorec_entry_t *dae,
|
||||
dvr_prio_t pri);
|
||||
void dvr_entry_created(dvr_entry_t *de);
|
||||
|
||||
dvr_entry_t *dvr_entry_create
|
||||
(const char *dvr_config_name,
|
||||
channel_t *ch, time_t start, time_t stop,
|
||||
time_t start_extra, time_t stop_extra,
|
||||
const char *title, const char *description, const char *lang,
|
||||
epg_genre_t *content_type,
|
||||
const char *creator, dvr_autorec_entry_t *dae,
|
||||
dvr_prio_t pri);
|
||||
dvr_entry_t *
|
||||
dvr_entry_create ( const char *uuid, htsmsg_t *conf );
|
||||
|
||||
dvr_entry_t *dvr_entry_update
|
||||
(dvr_entry_t *de,
|
||||
const char* de_title, const char *de_desc, const char *lang,
|
||||
time_t de_start, time_t de_stop,
|
||||
time_t de_start_extra, time_t de_stop_extra );
|
||||
|
||||
dvr_entry_t *
|
||||
dvr_entry_create_by_event( const char *dvr_config_uuid,
|
||||
epg_broadcast_t *e,
|
||||
time_t start_extra, time_t stop_extra,
|
||||
const char *creator,
|
||||
dvr_autorec_entry_t *dae,
|
||||
dvr_prio_t pri );
|
||||
|
||||
dvr_entry_t *
|
||||
dvr_entry_create_htsp( const char *dvr_config_uuid,
|
||||
channel_t *ch, time_t start, time_t stop,
|
||||
time_t start_extra, time_t stop_extra,
|
||||
const char *title, const char *description,
|
||||
const char *lang, epg_genre_t *content_type,
|
||||
const char *creator, dvr_autorec_entry_t *dae,
|
||||
dvr_prio_t pri );
|
||||
|
||||
dvr_entry_t *
|
||||
dvr_entry_update( dvr_entry_t *de,
|
||||
const char* de_title, const char *de_desc, const char *lang,
|
||||
time_t de_start, time_t de_stop,
|
||||
time_t de_start_extra, time_t de_stop_extra );
|
||||
|
||||
void dvr_init(void);
|
||||
void dvr_config_init(void);
|
||||
|
||||
void dvr_done(void);
|
||||
|
||||
|
@ -321,6 +367,9 @@ void dvr_event_updated(epg_broadcast_t *e);
|
|||
|
||||
dvr_entry_t *dvr_entry_find_by_id(int id);
|
||||
|
||||
static inline dvr_entry_t *dvr_entry_find_by_uuid(const char *uuid)
|
||||
{ return (dvr_entry_t*)idnode_find(uuid, &dvr_entry_class); }
|
||||
|
||||
dvr_entry_t *dvr_entry_find_by_event(epg_broadcast_t *e);
|
||||
|
||||
dvr_entry_t *dvr_entry_find_by_event_fuzzy(epg_broadcast_t *e);
|
||||
|
@ -333,34 +382,14 @@ dvr_entry_t *dvr_entry_cancel(dvr_entry_t *de);
|
|||
|
||||
void dvr_entry_dec_ref(dvr_entry_t *de);
|
||||
|
||||
void dvr_storage_set(dvr_config_t *cfg, const char *storage);
|
||||
|
||||
void dvr_charset_set(dvr_config_t *cfg, const char *charset);
|
||||
|
||||
void dvr_container_set(dvr_config_t *cfg, const char *container);
|
||||
|
||||
void dvr_file_permissions_set(dvr_config_t *cfg, int permissions);
|
||||
|
||||
void dvr_directory_permissions_set(dvr_config_t *cfg, int permissions);
|
||||
|
||||
void dvr_mux_cache_set(dvr_config_t *cfg, int mcache);
|
||||
|
||||
void dvr_postproc_set(dvr_config_t *cfg, const char *postproc);
|
||||
|
||||
void dvr_retention_set(dvr_config_t *cfg, int days);
|
||||
|
||||
void dvr_flags_set(dvr_config_t *cfg, int flags);
|
||||
|
||||
void dvr_mux_flags_set(dvr_config_t *cfg, int flags);
|
||||
|
||||
void dvr_extra_time_pre_set(dvr_config_t *cfg, int d);
|
||||
|
||||
void dvr_extra_time_post_set(dvr_config_t *cfg, int d);
|
||||
|
||||
void dvr_entry_delete(dvr_entry_t *de);
|
||||
|
||||
void dvr_entry_cancel_delete(dvr_entry_t *de);
|
||||
|
||||
htsmsg_t *dvr_entry_class_pri_list(void *o);
|
||||
htsmsg_t *dvr_entry_class_config_name_list(void *o);
|
||||
htsmsg_t *dvr_entry_class_duration_list(void *o, const char *not_set, int max);
|
||||
|
||||
/**
|
||||
* Query interface
|
||||
*/
|
||||
|
@ -386,15 +415,23 @@ int dvr_sort_start_ascending(const void *A, const void *B);
|
|||
/**
|
||||
*
|
||||
*/
|
||||
void dvr_autorec_add(const char *dvr_config_name,
|
||||
const char *title, const char *channel,
|
||||
const char *tag, epg_genre_t *content_type,
|
||||
const int min_duration, const int max_duration,
|
||||
const char *creator, const char *comment);
|
||||
|
||||
void dvr_autorec_add_series_link(const char *dvr_config_name,
|
||||
epg_broadcast_t *event,
|
||||
const char *creator, const char *comment);
|
||||
dvr_autorec_entry_t *
|
||||
dvr_autorec_create(const char *uuid, htsmsg_t *conf);
|
||||
|
||||
dvr_autorec_entry_t *
|
||||
dvr_autorec_add_series_link(const char *dvr_config_name,
|
||||
epg_broadcast_t *event,
|
||||
const char *creator, const char *comment);
|
||||
|
||||
void dvr_autorec_save(dvr_autorec_entry_t *dae);
|
||||
|
||||
void dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge);
|
||||
|
||||
static inline dvr_autorec_entry_t *
|
||||
dvr_autorec_find_by_uuid(const char *uuid)
|
||||
{ return (dvr_autorec_entry_t*)idnode_find(uuid, &dvr_autorec_entry_class); }
|
||||
|
||||
|
||||
void dvr_autorec_check_event(epg_broadcast_t *e);
|
||||
void dvr_autorec_check_brand(epg_brand_t *b);
|
||||
|
@ -404,7 +441,7 @@ void dvr_autorec_check_serieslink(epg_serieslink_t *s);
|
|||
|
||||
void autorec_destroy_by_channel(channel_t *ch, int delconf);
|
||||
|
||||
dvr_autorec_entry_t *autorec_entry_find(const char *id, int create);
|
||||
void autorec_destroy_by_channel_tag(channel_tag_t *ct, int delconf);
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
File diff suppressed because it is too large
Load diff
2336
src/dvr/dvr_db.c
2336
src/dvr/dvr_db.c
File diff suppressed because it is too large
Load diff
|
@ -115,17 +115,16 @@ void dvr_inotify_add ( dvr_entry_t *de )
|
|||
SKEL_USED(dvr_inotify_entry_skel);
|
||||
e->path = strdup(e->path);
|
||||
e->fd = inotify_add_watch(_inot_fd, e->path, EVENT_MASK);
|
||||
if (e->fd == -1) {
|
||||
tvhlog(LOG_ERR, "dvr", "failed to add inotify watch to %s (err=%s)",
|
||||
e->path, strerror(errno));
|
||||
free(path);
|
||||
dvr_inotify_del(de);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LIST_INSERT_HEAD(&e->entries, de, de_inotify_link);
|
||||
|
||||
if (e->fd < 0) {
|
||||
tvhlog(LOG_ERR, "dvr", "failed to add inotify watch to %s (err=%s)",
|
||||
e->path, strerror(errno));
|
||||
dvr_inotify_del(de);
|
||||
}
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
|
@ -146,7 +145,8 @@ void dvr_inotify_del ( dvr_entry_t *de )
|
|||
LIST_REMOVE(det, de_inotify_link);
|
||||
if (LIST_FIRST(&e->entries) == NULL) {
|
||||
RB_REMOVE(&_inot_tree, e, link);
|
||||
inotify_rm_watch(_inot_fd, e->fd);
|
||||
if (e->fd >= 0)
|
||||
inotify_rm_watch(_inot_fd, e->fd);
|
||||
free(e->path);
|
||||
free(e);
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ _dvr_inotify_find2
|
|||
snprintf(path, sizeof(path), "%s/%s", die->path, name);
|
||||
|
||||
LIST_FOREACH(de, &die->entries, de_inotify_link)
|
||||
if (!strcmp(path, de->de_filename))
|
||||
if (de->de_filename && !strcmp(path, de->de_filename))
|
||||
break;
|
||||
|
||||
return de;
|
||||
|
@ -211,7 +211,7 @@ _dvr_inotify_moved
|
|||
dvr_inotify_del(de);
|
||||
|
||||
htsp_dvr_entry_update(de);
|
||||
dvr_entry_notify(de);
|
||||
idnode_notify_simple(&de->de_id);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -239,7 +239,7 @@ _dvr_inotify_moved_all
|
|||
|
||||
while ((de = LIST_FIRST(&die->entries))) {
|
||||
htsp_dvr_entry_update(de);
|
||||
dvr_entry_notify(de);
|
||||
idnode_notify_simple(&de->de_id);
|
||||
dvr_inotify_del(de);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ dvr_rec_subscribe(dvr_entry_t *de)
|
|||
|
||||
snprintf(buf, sizeof(buf), "DVR: %s", lang_str_get(de->de_title, NULL));
|
||||
|
||||
if(de->de_mc == MC_PASS) {
|
||||
if(dvr_entry_get_mc(de) == MC_PASS) {
|
||||
streaming_queue_init(&de->de_sq, SMT_PACKET);
|
||||
de->de_gh = NULL;
|
||||
de->de_tsfix = NULL;
|
||||
|
@ -126,7 +126,7 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode)
|
|||
static char *
|
||||
cleanup_filename(char *s, dvr_config_t *cfg)
|
||||
{
|
||||
int i, len = strlen(s), dvr_flags = cfg->dvr_flags;
|
||||
int i, len = strlen(s);
|
||||
char *s1;
|
||||
|
||||
s1 = intlconv_utf8safestr(cfg->dvr_charset_id, s, len * 2);
|
||||
|
@ -148,11 +148,11 @@ cleanup_filename(char *s, dvr_config_t *cfg)
|
|||
if(s[i] == '/')
|
||||
s[i] = '-';
|
||||
|
||||
else if((dvr_flags & DVR_WHITESPACE_IN_TITLE) &&
|
||||
else if(cfg->dvr_whitespace_in_title &&
|
||||
(s[i] == ' ' || s[i] == '\t'))
|
||||
s[i] = '-';
|
||||
|
||||
else if((dvr_flags & DVR_CLEAN_TITLE) &&
|
||||
else if(cfg->dvr_clean_title &&
|
||||
((s[i] < 32) || (s[i] > 122) ||
|
||||
(strchr("/:\\<>|*?'\"", s[i]) != NULL)))
|
||||
s[i] = '_';
|
||||
|
@ -177,7 +177,10 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
struct stat st;
|
||||
char *filename, *s;
|
||||
struct tm tm;
|
||||
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
|
||||
dvr_config_t *cfg = de->de_config;
|
||||
|
||||
if (de == NULL)
|
||||
return -1;
|
||||
|
||||
strncpy(path, cfg->dvr_storage, sizeof(path));
|
||||
path[sizeof(path)-1] = '\0';
|
||||
|
@ -187,7 +190,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
path[strlen(path)-1] = '\0';
|
||||
|
||||
/* Append per-day directory */
|
||||
if (cfg->dvr_flags & DVR_DIR_PER_DAY) {
|
||||
if (cfg->dvr_dir_per_day) {
|
||||
localtime_r(&de->de_start, &tm);
|
||||
strftime(fullname, sizeof(fullname), "%F", &tm);
|
||||
s = cleanup_filename(fullname, cfg);
|
||||
|
@ -198,7 +201,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
}
|
||||
|
||||
/* Append per-channel directory */
|
||||
if (cfg->dvr_flags & DVR_DIR_PER_CHANNEL) {
|
||||
if (cfg->dvr_channel_dir) {
|
||||
char *chname = strdup(DVR_CH_NAME(de));
|
||||
s = cleanup_filename(chname, cfg);
|
||||
free(chname);
|
||||
|
@ -211,7 +214,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
// TODO: per-brand, per-season
|
||||
|
||||
/* Append per-title directory */
|
||||
if (cfg->dvr_flags & DVR_DIR_PER_TITLE) {
|
||||
if (cfg->dvr_title_dir) {
|
||||
char *title = strdup(lang_str_get(de->de_title, NULL));
|
||||
s = cleanup_filename(title, cfg);
|
||||
free(title);
|
||||
|
@ -292,7 +295,7 @@ dvr_rec_set_state(dvr_entry_t *de, dvr_rs_state_t newstate, int error)
|
|||
de->de_errors++;
|
||||
}
|
||||
if (notify)
|
||||
dvr_entry_notify(de);
|
||||
idnode_notify_simple(&de->de_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -304,10 +307,15 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
const source_info_t *si = &ss->ss_si;
|
||||
const streaming_start_component_t *ssc;
|
||||
int i;
|
||||
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
|
||||
dvr_config_t *cfg = de->de_config;
|
||||
muxer_container_type_t mc;
|
||||
|
||||
mc = de->de_mc;
|
||||
if (!cfg) {
|
||||
dvr_rec_fatal_error(de, "Unable to determine config profile");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mc = dvr_entry_get_mc(de);
|
||||
|
||||
de->de_mux = muxer_create(mc, &cfg->dvr_muxcnf);
|
||||
if(!de->de_mux) {
|
||||
|
@ -330,7 +338,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if(cfg->dvr_flags & DVR_TAG_FILES && de->de_bcast) {
|
||||
if(cfg->dvr_tag_files && de->de_bcast) {
|
||||
if(muxer_write_meta(de->de_mux, de->de_bcast)) {
|
||||
dvr_rec_fatal_error(de, "Unable to write meta data");
|
||||
return -1;
|
||||
|
@ -427,13 +435,13 @@ static void *
|
|||
dvr_thread(void *aux)
|
||||
{
|
||||
dvr_entry_t *de = aux;
|
||||
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
|
||||
dvr_config_t *cfg = de->de_config;
|
||||
streaming_queue_t *sq = &de->de_sq;
|
||||
streaming_message_t *sm;
|
||||
th_pkt_t *pkt;
|
||||
int run = 1;
|
||||
int started = 0;
|
||||
int comm_skip = (cfg->dvr_flags & DVR_SKIP_COMMERCIALS);
|
||||
int comm_skip = cfg->dvr_skip_commercials;
|
||||
int commercial = COMMERCIAL_UNKNOWN;
|
||||
|
||||
pthread_mutex_lock(&sq->sq_mutex);
|
||||
|
@ -508,9 +516,8 @@ dvr_thread(void *aux)
|
|||
dvr_rec_set_state(de, DVR_RS_WAIT_PROGRAM_START, 0);
|
||||
if(dvr_rec_start(de, sm->sm_data) == 0) {
|
||||
started = 1;
|
||||
dvr_entry_notify(de);
|
||||
idnode_changed(&de->de_id);
|
||||
htsp_dvr_entry_update(de);
|
||||
dvr_entry_save(de);
|
||||
}
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
}
|
||||
|
@ -666,7 +673,7 @@ dvr_thread_epilog(dvr_entry_t *de)
|
|||
muxer_destroy(de->de_mux);
|
||||
de->de_mux = NULL;
|
||||
|
||||
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
|
||||
if(cfg->dvr_postproc && de->de_filename)
|
||||
dvr_config_t *cfg = de->de_config;
|
||||
if(cfg && cfg->dvr_postproc && de->de_filename)
|
||||
dvr_spawn_postproc(de,cfg->dvr_postproc);
|
||||
}
|
||||
|
|
|
@ -2190,8 +2190,8 @@ htsmsg_t *epg_genres_list_all ( int major_only, int major_prefix )
|
|||
for (j = 0; j < (major_only ? 1 : 16); j++) {
|
||||
if (_epg_genre_names[i][j]) {
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_u32(e, "code", i << 4 | j);
|
||||
htsmsg_add_str(e, "name", _epg_genre_names[i][j]);
|
||||
htsmsg_add_u32(e, "key", major_only ? i : (i << 4 | j));
|
||||
htsmsg_add_str(e, "val", _epg_genre_names[i][j]);
|
||||
// TODO: use major_prefix
|
||||
htsmsg_add_msg(m, NULL, e);
|
||||
}
|
||||
|
|
|
@ -417,4 +417,5 @@ void epggrab_done ( void )
|
|||
epggrab_cron = NULL;
|
||||
free(epggrab_cron_multi);
|
||||
epggrab_cron_multi = NULL;
|
||||
epggrab_channel_done();
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
SKEL_DECLARE(epggrab_channel_skel, epggrab_channel_t);
|
||||
|
||||
/* **************************************************************************
|
||||
* EPG Grab Channel functions
|
||||
* *************************************************************************/
|
||||
|
@ -189,13 +191,12 @@ epggrab_channel_t *epggrab_channel_find
|
|||
{
|
||||
char *s;
|
||||
epggrab_channel_t *ec;
|
||||
static epggrab_channel_t *skel = NULL;
|
||||
if (!skel) skel = calloc(1, sizeof(epggrab_channel_t));
|
||||
skel->id = tvh_strdupa(id);
|
||||
|
||||
SKEL_ALLOC(epggrab_channel_skel);
|
||||
s = epggrab_channel_skel->id = tvh_strdupa(id);
|
||||
|
||||
/* Replace / with # */
|
||||
// Note: this is a bit of a nasty fix for #1774, but will do for now
|
||||
s = skel->id;
|
||||
while (*s) {
|
||||
if (*s == '/') *s = '#';
|
||||
s++;
|
||||
|
@ -203,18 +204,19 @@ epggrab_channel_t *epggrab_channel_find
|
|||
|
||||
/* Find */
|
||||
if (!create) {
|
||||
ec = RB_FIND(tree, skel, link, _ch_id_cmp);
|
||||
ec = RB_FIND(tree, epggrab_channel_skel, link, _ch_id_cmp);
|
||||
|
||||
/* Find/Create */
|
||||
} else {
|
||||
ec = RB_INSERT_SORTED(tree, skel, link, _ch_id_cmp);
|
||||
ec = RB_INSERT_SORTED(tree, epggrab_channel_skel, link, _ch_id_cmp);
|
||||
if (!ec) {
|
||||
assert(owner);
|
||||
ec = skel;
|
||||
ec->id = strdup(skel->id);
|
||||
ec = epggrab_channel_skel;
|
||||
SKEL_USED(epggrab_channel_skel);
|
||||
ec->id = strdup(ec->id);
|
||||
ec->mod = owner;
|
||||
skel = NULL;
|
||||
*save = 1;
|
||||
return ec;
|
||||
}
|
||||
}
|
||||
return ec;
|
||||
|
@ -299,3 +301,9 @@ epggrab_channel_is_ota ( epggrab_channel_t *ec )
|
|||
{
|
||||
return ec->mod->type == EPGGRAB_OTA;
|
||||
}
|
||||
|
||||
void
|
||||
epggrab_channel_done( void )
|
||||
{
|
||||
SKEL_FREE(epggrab_channel_skel);
|
||||
}
|
||||
|
|
|
@ -713,7 +713,7 @@ static int _eit_tune
|
|||
// consider changeing it?
|
||||
for (osl = RB_FIRST(&map->om_svcs); osl != NULL; osl = nxt) {
|
||||
nxt = RB_NEXT(osl, link);
|
||||
/* rule: if 5 mux scans fails for this service, remove it */
|
||||
/* rule: if 5 mux scans fail for this service, remove it */
|
||||
if (osl->last_tune_count + 5 <= map->om_tune_count ||
|
||||
!(s = mpegts_service_find_by_uuid(osl->uuid))) {
|
||||
epggrab_ota_service_del(map, om, osl, 1);
|
||||
|
|
|
@ -416,13 +416,13 @@ epggrab_ota_kick_cb ( void *p )
|
|||
[MM_EPG_DISABLE] = NULL,
|
||||
[MM_EPG_ENABLE] = NULL,
|
||||
[MM_EPG_FORCE] = NULL,
|
||||
[MM_EPG_FORCE_EIT] = "eit",
|
||||
[MM_EPG_FORCE_UK_FREESAT] = "uk_freesat",
|
||||
[MM_EPG_FORCE_UK_FREEVIEW] = "uk_freeview",
|
||||
[MM_EPG_FORCE_VIASAT_BALTIC] = "viasat_baltic",
|
||||
[MM_EPG_FORCE_OPENTV_SKY_UK] = "opentv-skyuk",
|
||||
[MM_EPG_FORCE_OPENTV_SKY_ITALIA] = "opentv-skyit",
|
||||
[MM_EPG_FORCE_OPENTV_SKY_AUSAT] = "opentv-ausat",
|
||||
[MM_EPG_ONLY_EIT] = "eit",
|
||||
[MM_EPG_ONLY_UK_FREESAT] = "uk_freesat",
|
||||
[MM_EPG_ONLY_UK_FREEVIEW] = "uk_freeview",
|
||||
[MM_EPG_ONLY_VIASAT_BALTIC] = "viasat_baltic",
|
||||
[MM_EPG_ONLY_OPENTV_SKY_UK] = "opentv-skyuk",
|
||||
[MM_EPG_ONLY_OPENTV_SKY_ITALIA] = "opentv-skyit",
|
||||
[MM_EPG_ONLY_OPENTV_SKY_AUSAT] = "opentv-ausat",
|
||||
};
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
@ -476,26 +476,25 @@ next_one:
|
|||
goto done;
|
||||
}
|
||||
|
||||
if (epg_flag != MM_EPG_FORCE) {
|
||||
/* Check we have modules attached and enabled */
|
||||
LIST_FOREACH(map, &om->om_modules, om_link) {
|
||||
if (map->om_module->tune(map, om, mm))
|
||||
break;
|
||||
}
|
||||
if (!map) {
|
||||
char name[256];
|
||||
mpegts_mux_nice_name(mm, name, sizeof(name));
|
||||
tvhdebug("epggrab", "no OTA modules active for %s, check again next time", name);
|
||||
goto done;
|
||||
/* Check we have modules attached and enabled */
|
||||
i = r = 0;
|
||||
LIST_FOREACH(map, &om->om_modules, om_link) {
|
||||
if (map->om_module->tune(map, om, mm)) {
|
||||
i++;
|
||||
if (modname && !strcmp(modname, map->om_module->id))
|
||||
r = 1;
|
||||
}
|
||||
}
|
||||
if ((i == 0 || (r == 0 && modname)) && epg_flag != MM_EPG_FORCE) {
|
||||
char name[256];
|
||||
mpegts_mux_nice_name(mm, name, sizeof(name));
|
||||
tvhdebug("epggrab", "no OTA modules active for %s, check again next time", name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Some init stuff */
|
||||
free(om->om_force_modname);
|
||||
if (modname)
|
||||
om->om_force_modname = strdup(modname);
|
||||
else
|
||||
om->om_force_modname = NULL;
|
||||
om->om_force_modname = modname ? strdup(modname) : NULL;
|
||||
|
||||
/* Subscribe to the mux */
|
||||
if ((r = mpegts_mux_subscribe(mm, "epggrab", SUBSCRIPTION_PRIO_EPG))) {
|
||||
|
@ -555,6 +554,8 @@ epggrab_ota_start_cb ( void *p )
|
|||
pthread_mutex_lock(&epggrab_ota_mutex);
|
||||
if (!cron_multi_next(epggrab_ota_cron_multi, dispatch_clock, &next))
|
||||
epggrab_ota_next_arm(next);
|
||||
else
|
||||
tvhwarn("epggrab", "ota cron config invalid or unset");
|
||||
pthread_mutex_unlock(&epggrab_ota_mutex);
|
||||
}
|
||||
|
||||
|
@ -570,6 +571,8 @@ epggrab_ota_arm ( time_t last )
|
|||
if (last != (time_t)-1 && last + 1800 > next)
|
||||
next = last + 1800;
|
||||
epggrab_ota_next_arm(next);
|
||||
} else {
|
||||
tvhwarn("epggrab", "ota cron config invalid or unset");
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&epggrab_ota_mutex);
|
||||
|
@ -615,6 +618,7 @@ epggrab_ota_service_add ( epggrab_ota_map_t *map, epggrab_ota_mux_t *ota,
|
|||
ota->om_save = 1;
|
||||
epggrab_ota_service_trace(ota, svcl, "add new");
|
||||
}
|
||||
svcl->last_tune_count = map->om_tune_count;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -724,8 +728,8 @@ epggrab_ota_init ( void )
|
|||
|
||||
epggrab_ota_initial = 1;
|
||||
epggrab_ota_timeout = 600;
|
||||
epggrab_ota_cron = strdup("# Default config (02:04 and 14:04 everyday)\n4 2 * * *\n4 14 * * *");;
|
||||
epggrab_ota_cron_multi = NULL;
|
||||
epggrab_ota_cron = strdup("# Default config (02:04 and 14:04 everyday)\n4 2 * * *\n4 14 * * *");
|
||||
epggrab_ota_cron_multi = cron_multi_set(epggrab_ota_cron);
|
||||
epggrab_ota_pending_flag = 0;
|
||||
|
||||
RB_INIT(&epggrab_ota_all);
|
||||
|
|
|
@ -54,6 +54,8 @@ 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_done(void);
|
||||
|
||||
/* **************************************************************************
|
||||
* Internal module routines
|
||||
* *************************************************************************/
|
||||
|
|
|
@ -587,6 +587,7 @@ esfilter_class_action_enum(void *o)
|
|||
const idclass_t esfilter_class = {
|
||||
.ic_class = "esfilter",
|
||||
.ic_caption = "Elementary Stream Filter",
|
||||
.ic_event = "esfilter",
|
||||
.ic_save = esfilter_class_save,
|
||||
.ic_get_title = esfilter_class_get_title,
|
||||
.ic_delete = esfilter_class_delete,
|
||||
|
@ -1034,7 +1035,7 @@ esfilter_init(void)
|
|||
for (i = 0; i <= ESF_CLASS_LAST; i++)
|
||||
TAILQ_INIT(&esfilters[i]);
|
||||
|
||||
if (!(c = hts_settings_load_r(1, "esfilter")))
|
||||
if (!(c = hts_settings_load("esfilter")))
|
||||
return;
|
||||
HTSMSG_FOREACH(f, c) {
|
||||
if (!(e = htsmsg_field_get_map(f)))
|
||||
|
|
19
src/htsmsg.c
19
src/htsmsg.c
|
@ -261,6 +261,25 @@ htsmsg_add_str(htsmsg_t *msg, const char *name, const char *str)
|
|||
f->hmf_str = strdup(str);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int
|
||||
htsmsg_set_str(htsmsg_t *msg, const char *name, const char *str)
|
||||
{
|
||||
htsmsg_field_t *f = htsmsg_field_find(msg, name);
|
||||
if (!f)
|
||||
f = htsmsg_field_add(msg, name, HMF_STR, HMF_ALLOCED | HMF_NAME_ALLOCED);
|
||||
else {
|
||||
if (f->hmf_type != HMF_STR)
|
||||
return 1;
|
||||
if(f->hmf_flags & HMF_ALLOCED)
|
||||
free((void *)f->hmf_str);
|
||||
}
|
||||
f->hmf_str = strdup(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -135,6 +135,11 @@ void htsmsg_add_s64(htsmsg_t *msg, const char *name, int64_t s64);
|
|||
*/
|
||||
void htsmsg_add_str(htsmsg_t *msg, const char *name, const char *str);
|
||||
|
||||
/**
|
||||
* Add/update a string field
|
||||
*/
|
||||
int htsmsg_set_str(htsmsg_t *msg, const char *name, const char *str);
|
||||
|
||||
/**
|
||||
* Add an field where source is a list or map message.
|
||||
*/
|
||||
|
|
|
@ -550,13 +550,16 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp)
|
|||
channel_tag_t *ct;
|
||||
service_t *t;
|
||||
epg_broadcast_t *now, *next = NULL;
|
||||
int64_t chnum = channel_get_number(ch);
|
||||
|
||||
htsmsg_t *out = htsmsg_create_map();
|
||||
htsmsg_t *tags = htsmsg_create_list();
|
||||
htsmsg_t *services = htsmsg_create_list();
|
||||
|
||||
htsmsg_add_u32(out, "channelId", channel_get_id(ch));
|
||||
htsmsg_add_u32(out, "channelNumber", channel_get_number(ch));
|
||||
htsmsg_add_u32(out, "channelNumber", channel_get_major(chnum));
|
||||
if (channel_get_minor(chnum))
|
||||
htsmsg_add_u32(out, "channelNumberMinor", channel_get_minor(chnum));
|
||||
|
||||
htsmsg_add_str(out, "channelName", channel_get_name(ch));
|
||||
if(ch->ch_icon != NULL) {
|
||||
|
@ -652,9 +655,8 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
|
|||
htsmsg_t *out = htsmsg_create_map();
|
||||
const char *s = NULL, *error = NULL;
|
||||
const char *p;
|
||||
dvr_config_t *cfg;
|
||||
|
||||
htsmsg_add_u32(out, "id", de->de_id);
|
||||
htsmsg_add_u32(out, "id", idnode_get_short_uuid(&de->de_id));
|
||||
if (de->de_channel)
|
||||
htsmsg_add_u32(out, "channel", channel_get_id(de->de_channel));
|
||||
|
||||
|
@ -666,11 +668,9 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
|
|||
if( de->de_desc && (s = lang_str_get(de->de_desc, NULL)))
|
||||
htsmsg_add_str(out, "description", s);
|
||||
|
||||
if( de->de_filename && de->de_config_name ) {
|
||||
if ((cfg = dvr_config_find_by_name_default(de->de_config_name))) {
|
||||
if ((p = tvh_strbegins(de->de_filename, cfg->dvr_storage)))
|
||||
htsmsg_add_str(out, "path", p);
|
||||
}
|
||||
if( de->de_filename && de->de_config ) {
|
||||
if ((p = tvh_strbegins(de->de_filename, de->de_config->dvr_storage)))
|
||||
htsmsg_add_str(out, "path", p);
|
||||
}
|
||||
|
||||
switch(de->de_sched_state) {
|
||||
|
@ -796,7 +796,7 @@ htsp_build_event
|
|||
}
|
||||
|
||||
if((de = dvr_entry_find_by_event(e)) != NULL) {
|
||||
htsmsg_add_u32(out, "dvrId", de->de_id);
|
||||
htsmsg_add_u32(out, "dvrId", idnode_get_short_uuid(&de->de_id));
|
||||
}
|
||||
|
||||
if ((n = epg_broadcast_get_next(e)))
|
||||
|
@ -1217,9 +1217,9 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
desc = "";
|
||||
|
||||
// create the dvr entry
|
||||
de = dvr_entry_create(dvr_config_name, ch, start, stop,
|
||||
start_extra, stop_extra,
|
||||
title, desc, lang, 0, creator, NULL, priority);
|
||||
de = dvr_entry_create_htsp(dvr_config_name, ch, start, stop,
|
||||
start_extra, stop_extra,
|
||||
title, desc, lang, 0, creator, NULL, priority);
|
||||
|
||||
/* Event timer */
|
||||
} else {
|
||||
|
@ -1238,7 +1238,7 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
case DVR_RECORDING:
|
||||
case DVR_MISSED_TIME:
|
||||
case DVR_COMPLETED:
|
||||
htsmsg_add_u32(out, "id", de->de_id);
|
||||
htsmsg_add_u32(out, "id", idnode_get_short_uuid(&de->de_id));
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
break;
|
||||
case DVR_NOSTATE:
|
||||
|
@ -2531,7 +2531,7 @@ void
|
|||
htsp_dvr_entry_delete(dvr_entry_t *de)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_u32(m, "id", de->de_id);
|
||||
htsmsg_add_u32(m, "id", idnode_get_short_uuid(&de->de_id));
|
||||
htsmsg_add_str(m, "method", "dvrEntryDelete");
|
||||
htsp_async_send(m, HTSP_ASYNC_ON);
|
||||
}
|
||||
|
|
43
src/http.c
43
src/http.c
|
@ -324,9 +324,13 @@ http_error(http_connection_t *hc, int error)
|
|||
"<HTML><HEAD>\r\n"
|
||||
"<TITLE>%d %s</TITLE>\r\n"
|
||||
"</HEAD><BODY>\r\n"
|
||||
"<H1>%d %s</H1>\r\n"
|
||||
"</BODY></HTML>\r\n",
|
||||
error, errtxt, error, errtxt);
|
||||
"<H1>%d %s</H1>\r\n",
|
||||
error, errtxt, error, errtxt);
|
||||
|
||||
if (error == HTTP_STATUS_UNAUTHORIZED)
|
||||
htsbuf_qprintf(&hc->hc_reply, "<P><A HREF=\"/\">Default Login</A></P>");
|
||||
|
||||
htsbuf_qprintf(&hc->hc_reply, "</BODY></HTML>\r\n");
|
||||
|
||||
http_send_reply(hc, error, "text/html", NULL, NULL, 0);
|
||||
}
|
||||
|
@ -403,11 +407,14 @@ static int http_access_verify_ticket(http_connection_t *hc)
|
|||
{
|
||||
const char *ticket_id = http_arg_get(&hc->hc_req_args, "ticket");
|
||||
|
||||
if (hc->hc_ticket)
|
||||
return 0;
|
||||
if(!access_ticket_verify(ticket_id, hc->hc_url)) {
|
||||
char addrstr[50];
|
||||
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrstr, 50);
|
||||
tvhlog(LOG_INFO, "HTTP", "%s: using ticket %s for %s",
|
||||
addrstr, ticket_id, hc->hc_url);
|
||||
hc->hc_ticket = 1;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
|
@ -422,8 +429,14 @@ http_access_verify(http_connection_t *hc, int mask)
|
|||
if (!http_access_verify_ticket(hc))
|
||||
return 0;
|
||||
|
||||
return access_verify(hc->hc_username, hc->hc_password,
|
||||
(struct sockaddr *)hc->hc_peer, mask);
|
||||
if (hc->hc_access == NULL) {
|
||||
hc->hc_access = access_get(hc->hc_username, hc->hc_password,
|
||||
(struct sockaddr *)hc->hc_peer);
|
||||
if (hc->hc_access == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return access_verify2(hc->hc_access, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -431,21 +444,24 @@ http_access_verify(http_connection_t *hc, int mask)
|
|||
*/
|
||||
int
|
||||
http_access_verify_channel(http_connection_t *hc, int mask,
|
||||
struct channel *ch)
|
||||
struct channel *ch, int ticket)
|
||||
{
|
||||
access_t *a;
|
||||
int res = -1;
|
||||
|
||||
assert(ch);
|
||||
|
||||
if (!http_access_verify_ticket(hc))
|
||||
if (ticket && !http_access_verify_ticket(hc))
|
||||
return 0;
|
||||
|
||||
a = access_get(hc->hc_username, hc->hc_password,
|
||||
(struct sockaddr *)hc->hc_peer);
|
||||
if (channel_access(ch, a, hc->hc_username))
|
||||
if (hc->hc_access == NULL) {
|
||||
hc->hc_access = access_get(hc->hc_username, hc->hc_password,
|
||||
(struct sockaddr *)hc->hc_peer);
|
||||
if (hc->hc_access == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (channel_access(ch, hc->hc_access, hc->hc_username))
|
||||
res = 0;
|
||||
access_destroy(a);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -464,6 +480,9 @@ http_exec(http_connection_t *hc, http_path_t *hp, char *remain)
|
|||
err = HTTP_STATUS_UNAUTHORIZED;
|
||||
else
|
||||
err = hp->hp_callback(hc, remain, hp->hp_opaque);
|
||||
access_destroy(hc->hc_access);
|
||||
hc->hc_access = NULL;
|
||||
hc->hc_ticket = 0;
|
||||
|
||||
if(err == -1)
|
||||
return 1;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "htsbuf.h"
|
||||
#include "url.h"
|
||||
#include "tvhpoll.h"
|
||||
#include "access.h"
|
||||
|
||||
struct channel;
|
||||
|
||||
|
@ -130,6 +131,8 @@ typedef struct http_connection {
|
|||
|
||||
char *hc_username;
|
||||
char *hc_password;
|
||||
access_t *hc_access;
|
||||
int hc_ticket;
|
||||
|
||||
struct config_head *hc_user_config;
|
||||
|
||||
|
@ -206,7 +209,8 @@ void http_server_register(void);
|
|||
void http_server_done(void);
|
||||
|
||||
int http_access_verify(http_connection_t *hc, int mask);
|
||||
int http_access_verify_channel(http_connection_t *hc, int mask, struct channel *ch);
|
||||
int http_access_verify_channel(http_connection_t *hc, int mask,
|
||||
struct channel *ch, int ticket);
|
||||
|
||||
void http_deescape(char *s);
|
||||
|
||||
|
|
416
src/idnode.c
416
src/idnode.c
|
@ -155,7 +155,7 @@ idnode_insert(idnode_t *in, const char *uuid, const idclass_t *class, int flags)
|
|||
idclass_register(class); // Note: we never actually unregister
|
||||
|
||||
/* Fire event */
|
||||
idnode_notify(in, NULL, 0, 1);
|
||||
idnode_notify_simple(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ idnode_unlink(idnode_t *in)
|
|||
lock_assert(&global_lock);
|
||||
RB_REMOVE(&idnodes, in, in_link);
|
||||
tvhtrace("idnode", "unlink node %s", idnode_uuid_as_str(in));
|
||||
idnode_notify(in, NULL, 0, 1);
|
||||
idnode_notify_simple(in);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,7 +227,7 @@ idnode_get_short_uuid (const idnode_t *in)
|
|||
const char *
|
||||
idnode_uuid_as_str(const idnode_t *in)
|
||||
{
|
||||
static tvh_uuid_t ret[16];
|
||||
static tvh_uuid_t __thread ret[16];
|
||||
static uint8_t p = 0;
|
||||
bin2hex(ret[p].hex, sizeof(ret[p].hex), in->in_uuid, sizeof(in->in_uuid));
|
||||
const char *s = ret[p].hex;
|
||||
|
@ -366,10 +366,94 @@ idnode_get_u32
|
|||
*u32 = *(int*)ptr;
|
||||
return 0;
|
||||
case PT_U16:
|
||||
*u32 = *(uint32_t*)ptr;
|
||||
*u32 = *(uint16_t*)ptr;
|
||||
return 0;
|
||||
case PT_U32:
|
||||
*u32 = *(uint16_t*)ptr;
|
||||
*u32 = *(uint32_t*)ptr;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get field as signed 64-bit int
|
||||
*/
|
||||
int
|
||||
idnode_get_s64
|
||||
( idnode_t *self, const char *key, int64_t *s64 )
|
||||
{
|
||||
const property_t *p = idnode_find_prop(self, key);
|
||||
if (p->islist) return 1;
|
||||
if (p) {
|
||||
const void *ptr;
|
||||
if (p->get)
|
||||
ptr = p->get(self);
|
||||
else
|
||||
ptr = ((void*)self) + p->off;
|
||||
switch (p->type) {
|
||||
case PT_INT:
|
||||
case PT_BOOL:
|
||||
*s64 = *(int*)ptr;
|
||||
return 0;
|
||||
case PT_U16:
|
||||
*s64 = *(uint16_t*)ptr;
|
||||
return 0;
|
||||
case PT_U32:
|
||||
*s64 = *(uint32_t*)ptr;
|
||||
return 0;
|
||||
case PT_S64:
|
||||
*s64 = *(int64_t*)ptr;
|
||||
return 0;
|
||||
case PT_DBL:
|
||||
*s64 = *(double*)ptr;
|
||||
return 0;
|
||||
case PT_TIME:
|
||||
*s64 = *(time_t*)ptr;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get field as double
|
||||
*/
|
||||
int
|
||||
idnode_get_dbl
|
||||
( idnode_t *self, const char *key, double *dbl )
|
||||
{
|
||||
const property_t *p = idnode_find_prop(self, key);
|
||||
if (p->islist) return 1;
|
||||
if (p) {
|
||||
const void *ptr;
|
||||
if (p->get)
|
||||
ptr = p->get(self);
|
||||
else
|
||||
ptr = ((void*)self) + p->off;
|
||||
switch (p->type) {
|
||||
case PT_INT:
|
||||
case PT_BOOL:
|
||||
*dbl = *(int*)ptr;
|
||||
return 0;
|
||||
case PT_U16:
|
||||
*dbl = *(uint16_t*)ptr;
|
||||
return 0;
|
||||
case PT_U32:
|
||||
*dbl = *(uint32_t*)ptr;
|
||||
return 0;
|
||||
case PT_S64:
|
||||
*dbl = *(int64_t*)ptr;
|
||||
return 0;
|
||||
case PT_DBL:
|
||||
*dbl = *(double *)ptr;
|
||||
return 0;
|
||||
case PT_TIME:
|
||||
*dbl = *(time_t*)ptr;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
|
@ -388,8 +472,11 @@ idnode_get_bool
|
|||
const property_t *p = idnode_find_prop(self, key);
|
||||
if (p->islist) return 1;
|
||||
if (p) {
|
||||
void *ptr = self;
|
||||
ptr += p->off;
|
||||
const void *ptr;
|
||||
if (p->get)
|
||||
ptr = p->get(self);
|
||||
else
|
||||
ptr = ((void*)self) + p->off;
|
||||
switch (p->type) {
|
||||
case PT_BOOL:
|
||||
*b = *(int*)ptr;
|
||||
|
@ -401,6 +488,32 @@ idnode_get_bool
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get field as time
|
||||
*/
|
||||
int
|
||||
idnode_get_time
|
||||
( idnode_t *self, const char *key, time_t *tm )
|
||||
{
|
||||
const property_t *p = idnode_find_prop(self, key);
|
||||
if (p->islist) return 1;
|
||||
if (p) {
|
||||
const void *ptr;
|
||||
if (p->get)
|
||||
ptr = p->get(self);
|
||||
else
|
||||
ptr = ((void*)self) + p->off;
|
||||
switch (p->type) {
|
||||
case PT_TIME:
|
||||
*tm = *(time_t*)ptr;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Lookup
|
||||
* *************************************************************************/
|
||||
|
@ -414,6 +527,8 @@ idnode_find(const char *uuid, const idclass_t *idc)
|
|||
idnode_t skel, *r;
|
||||
|
||||
tvhtrace("idnode", "find node %s class %s", uuid, idc ? idc->ic_class : NULL);
|
||||
if(uuid == NULL || strlen(uuid) != UUID_HEX_SIZE - 1)
|
||||
return NULL;
|
||||
if(hex2bin(skel.in_uuid, sizeof(skel.in_uuid), uuid))
|
||||
return NULL;
|
||||
r = RB_FIND(&idnodes, &skel, in_link, in_cmp);
|
||||
|
@ -464,6 +579,8 @@ idnode_cmp_title
|
|||
return strcmp(sa ?: "", sb ?: "");
|
||||
}
|
||||
|
||||
#define safecmp(a, b) ((a) > (b) ? 1 : ((a) < (b) ? -1 : 0))
|
||||
|
||||
static int
|
||||
idnode_cmp_sort
|
||||
( const void *a, const void *b, void *s )
|
||||
|
@ -493,36 +610,105 @@ idnode_cmp_sort
|
|||
{
|
||||
int r;
|
||||
const char *stra = tvh_strdupa(idnode_get_str(ina, sort->key) ?: "");
|
||||
const char *strb = idnode_get_str(inb, sort->key);
|
||||
const char *strb = idnode_get_str(inb, sort->key) ?: "";
|
||||
if (sort->dir == IS_ASC)
|
||||
r = strcmp(stra ?: "", strb ?: "");
|
||||
r = strcmp(stra, strb);
|
||||
else
|
||||
r = strcmp(strb ?: "", stra ?: "");
|
||||
r = strcmp(strb, stra);
|
||||
return r;
|
||||
}
|
||||
break;
|
||||
case PT_INT:
|
||||
case PT_U16:
|
||||
case PT_U32:
|
||||
case PT_BOOL:
|
||||
case PT_PERM:
|
||||
{
|
||||
int32_t i32a = 0, i32b = 0;
|
||||
idnode_get_u32(ina, sort->key, (uint32_t *)&i32a);
|
||||
idnode_get_u32(inb, sort->key, (uint32_t *)&i32b);
|
||||
if (sort->dir == IS_ASC)
|
||||
return safecmp(i32a, i32b);
|
||||
else
|
||||
return safecmp(i32b, i32a);
|
||||
}
|
||||
break;
|
||||
case PT_U32:
|
||||
{
|
||||
uint32_t u32a = 0, u32b = 0;
|
||||
idnode_get_u32(ina, sort->key, &u32a);
|
||||
idnode_get_u32(inb, sort->key, &u32b);
|
||||
if (sort->dir == IS_ASC)
|
||||
return u32a - u32b;
|
||||
return safecmp(u32a, u32b);
|
||||
else
|
||||
return u32b - u32a;
|
||||
return safecmp(u32b, u32a);
|
||||
}
|
||||
break;
|
||||
case PT_S64:
|
||||
{
|
||||
int64_t s64a = 0, s64b = 0;
|
||||
idnode_get_s64(ina, sort->key, &s64a);
|
||||
idnode_get_s64(inb, sort->key, &s64b);
|
||||
if (sort->dir == IS_ASC)
|
||||
return safecmp(s64a, s64b);
|
||||
else
|
||||
return safecmp(s64b, s64a);
|
||||
}
|
||||
break;
|
||||
case PT_DBL:
|
||||
// TODO
|
||||
{
|
||||
double dbla = 0, dblb = 0;
|
||||
idnode_get_dbl(ina, sort->key, &dbla);
|
||||
idnode_get_dbl(inb, sort->key, &dblb);
|
||||
if (sort->dir == IS_ASC)
|
||||
return safecmp(dbla, dblb);
|
||||
else
|
||||
return safecmp(dblb, dbla);
|
||||
}
|
||||
break;
|
||||
case PT_TIME:
|
||||
{
|
||||
time_t ta = 0, tb = 0;
|
||||
idnode_get_time(ina, sort->key, &ta);
|
||||
idnode_get_time(inb, sort->key, &tb);
|
||||
if (sort->dir == IS_ASC)
|
||||
return safecmp(ta, tb);
|
||||
else
|
||||
return safecmp(tb, ta);
|
||||
}
|
||||
break;
|
||||
case PT_LANGSTR:
|
||||
// TODO?
|
||||
case PT_NONE:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
idnode_filter_init
|
||||
( idnode_t *in, idnode_filter_t *filter )
|
||||
{
|
||||
idnode_filter_ele_t *f;
|
||||
const property_t *p;
|
||||
|
||||
LIST_FOREACH(f, filter, link) {
|
||||
if (f->type == IF_NUM) {
|
||||
p = idnode_find_prop(in, f->key);
|
||||
if (p) {
|
||||
if (p->type == PT_U32 || p->type == PT_S64 ||
|
||||
p->type == PT_TIME) {
|
||||
int64_t v = f->u.n.n;
|
||||
if (p->intsplit != f->u.n.intsplit) {
|
||||
v = (v / (f->u.n.intsplit <= 0 ? 1 : 0)) * p->intsplit;
|
||||
f->u.n.n = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
f->checked = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
idnode_filter
|
||||
( idnode_t *in, idnode_filter_t *filter )
|
||||
|
@ -530,6 +716,8 @@ idnode_filter
|
|||
idnode_filter_ele_t *f;
|
||||
|
||||
LIST_FOREACH(f, filter, link) {
|
||||
if (!f->checked)
|
||||
idnode_filter_init(in, filter);
|
||||
if (f->type == IF_STR) {
|
||||
const char *str;
|
||||
str = idnode_get_display(in, idnode_find_prop(in, f->key));
|
||||
|
@ -558,12 +746,32 @@ idnode_filter
|
|||
break;
|
||||
}
|
||||
} else if (f->type == IF_NUM || f->type == IF_BOOL) {
|
||||
uint32_t u32;
|
||||
int64_t a, b;
|
||||
if (idnode_get_u32(in, f->key, &u32))
|
||||
if (idnode_get_s64(in, f->key, &a))
|
||||
return 1;
|
||||
a = u32;
|
||||
b = (f->type == IF_NUM) ? f->u.n : f->u.b;
|
||||
b = (f->type == IF_NUM) ? f->u.n.n : f->u.b;
|
||||
switch (f->comp) {
|
||||
case IC_IN:
|
||||
case IC_RE:
|
||||
break; // Note: invalid
|
||||
case IC_EQ:
|
||||
if (a != b)
|
||||
return 1;
|
||||
break;
|
||||
case IC_LT:
|
||||
if (a > b)
|
||||
return 1;
|
||||
break;
|
||||
case IC_GT:
|
||||
if (a < b)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
} else if (f->type == IF_DBL) {
|
||||
double a, b;
|
||||
if (idnode_get_dbl(in, f->key, &a))
|
||||
return 1;
|
||||
b = f->u.dbl;
|
||||
switch (f->comp) {
|
||||
case IC_IN:
|
||||
case IC_RE:
|
||||
|
@ -607,13 +815,26 @@ idnode_filter_add_str
|
|||
|
||||
void
|
||||
idnode_filter_add_num
|
||||
( idnode_filter_t *filt, const char *key, int64_t val, int comp )
|
||||
( idnode_filter_t *filt, const char *key, int64_t val, int comp, int64_t intsplit )
|
||||
{
|
||||
idnode_filter_ele_t *ele = calloc(1, sizeof(idnode_filter_ele_t));
|
||||
ele->key = strdup(key);
|
||||
ele->type = IF_NUM;
|
||||
ele->comp = comp;
|
||||
ele->u.n = val;
|
||||
ele->u.n.n = val;
|
||||
ele->u.n.intsplit = intsplit;
|
||||
LIST_INSERT_HEAD(filt, ele, link);
|
||||
}
|
||||
|
||||
void
|
||||
idnode_filter_add_dbl
|
||||
( idnode_filter_t *filt, const char *key, double dbl, int comp )
|
||||
{
|
||||
idnode_filter_ele_t *ele = calloc(1, sizeof(idnode_filter_ele_t));
|
||||
ele->key = strdup(key);
|
||||
ele->type = IF_DBL;
|
||||
ele->comp = comp;
|
||||
ele->u.dbl = dbl;
|
||||
LIST_INSERT_HEAD(filt, ele, link);
|
||||
}
|
||||
|
||||
|
@ -742,7 +963,7 @@ idnode_write0 ( idnode_t *self, htsmsg_t *c, int optmask, int dosave )
|
|||
if (save && dosave)
|
||||
idnode_savefn(self);
|
||||
if (dosave)
|
||||
idnode_notify(self, NULL, 0, 0);
|
||||
idnode_notify_simple(self);
|
||||
// Note: always output event if "dosave", reason is that UI updates on
|
||||
// these, but there are some subtle cases where it will expect
|
||||
// an update and not get one. This include fields being set for
|
||||
|
@ -751,19 +972,23 @@ idnode_write0 ( idnode_t *self, htsmsg_t *c, int optmask, int dosave )
|
|||
return save;
|
||||
}
|
||||
|
||||
void
|
||||
idnode_changed( idnode_t *self )
|
||||
{
|
||||
idnode_notify_simple(self);
|
||||
idnode_savefn(self);
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Read
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* Save
|
||||
*/
|
||||
void
|
||||
idnode_read0 ( idnode_t *self, htsmsg_t *c, int optmask )
|
||||
idnode_read0 ( idnode_t *self, htsmsg_t *c, htsmsg_t *list, int optmask )
|
||||
{
|
||||
const idclass_t *idc = self->in_class;
|
||||
for (; idc; idc = idc->ic_super)
|
||||
prop_read_values(self, idc->ic_properties, c, optmask, NULL);
|
||||
prop_read_values(self, idc->ic_properties, c, list, optmask);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -771,11 +996,11 @@ idnode_read0 ( idnode_t *self, htsmsg_t *c, int optmask )
|
|||
*/
|
||||
static void
|
||||
add_params
|
||||
(struct idnode *self, const idclass_t *ic, htsmsg_t *p, int optmask, htsmsg_t *inc)
|
||||
(struct idnode *self, const idclass_t *ic, htsmsg_t *p, htsmsg_t *list, int optmask)
|
||||
{
|
||||
/* Parent first */
|
||||
if(ic->ic_super != NULL)
|
||||
add_params(self, ic->ic_super, p, optmask, inc);
|
||||
add_params(self, ic->ic_super, p, list, optmask);
|
||||
|
||||
/* Seperator (if not empty) */
|
||||
#if 0
|
||||
|
@ -788,14 +1013,14 @@ add_params
|
|||
#endif
|
||||
|
||||
/* Properties */
|
||||
prop_serialize(self, ic->ic_properties, p, optmask, inc);
|
||||
prop_serialize(self, ic->ic_properties, p, list, optmask);
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
idnode_params (const idclass_t *idc, idnode_t *self, int optmask)
|
||||
idnode_params (const idclass_t *idc, idnode_t *self, htsmsg_t *list, int optmask)
|
||||
{
|
||||
htsmsg_t *p = htsmsg_create_list();
|
||||
add_params(self, idc, p, optmask, NULL);
|
||||
add_params(self, idc, p, list, optmask);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -821,17 +1046,59 @@ idclass_get_class (const idclass_t *idc)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
idclass_get_event (const idclass_t *idc)
|
||||
{
|
||||
while (idc) {
|
||||
if (idc->ic_event)
|
||||
return idc->ic_event;
|
||||
idc = idc->ic_super;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
idclass_get_order (const idclass_t *idc)
|
||||
{
|
||||
while (idc) {
|
||||
if (idc->ic_class)
|
||||
if (idc->ic_order)
|
||||
return idc->ic_order;
|
||||
idc = idc->ic_super;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
idclass_get_property_groups (const idclass_t *idc)
|
||||
{
|
||||
const property_group_t *g;
|
||||
htsmsg_t *e, *m;
|
||||
int count;
|
||||
while (idc) {
|
||||
if (idc->ic_groups) {
|
||||
m = htsmsg_create_list();
|
||||
count = 0;
|
||||
for (g = idc->ic_groups; g->number && g->name; g++) {
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_u32(e, "number", g->number);
|
||||
htsmsg_add_str(e, "name", g->name);
|
||||
if (g->parent)
|
||||
htsmsg_add_u32(e, "parent", g->parent);
|
||||
if (g->column)
|
||||
htsmsg_add_u32(e, "column", g->column);
|
||||
htsmsg_add_msg(m, NULL, e);
|
||||
count++;
|
||||
}
|
||||
if (count)
|
||||
return m;
|
||||
htsmsg_destroy(m);
|
||||
break;
|
||||
}
|
||||
idc = idc->ic_super;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
ic_cmp ( const idclass_link_t *a, const idclass_link_t *b )
|
||||
{
|
||||
|
@ -870,7 +1137,7 @@ idclass_find ( const char *class )
|
|||
* Just get the class definition
|
||||
*/
|
||||
htsmsg_t *
|
||||
idclass_serialize0(const idclass_t *idc, int optmask)
|
||||
idclass_serialize0(const idclass_t *idc, htsmsg_t *list, int optmask)
|
||||
{
|
||||
const char *s;
|
||||
htsmsg_t *p, *m = htsmsg_create_map();
|
||||
|
@ -880,11 +1147,15 @@ idclass_serialize0(const idclass_t *idc, int optmask)
|
|||
htsmsg_add_str(m, "caption", s);
|
||||
if ((s = idclass_get_class(idc)))
|
||||
htsmsg_add_str(m, "class", s);
|
||||
if ((s = idclass_get_event(idc)))
|
||||
htsmsg_add_str(m, "event", s);
|
||||
if ((s = idclass_get_order(idc)))
|
||||
htsmsg_add_str(m, "order", s);
|
||||
if ((p = idclass_get_property_groups(idc)))
|
||||
htsmsg_add_msg(m, "groups", p);
|
||||
|
||||
/* Props */
|
||||
if ((p = idnode_params(idc, NULL, optmask)))
|
||||
if ((p = idnode_params(idc, NULL, list, optmask)))
|
||||
htsmsg_add_msg(m, "props", p);
|
||||
|
||||
return m;
|
||||
|
@ -894,7 +1165,7 @@ idclass_serialize0(const idclass_t *idc, int optmask)
|
|||
*
|
||||
*/
|
||||
htsmsg_t *
|
||||
idnode_serialize0(idnode_t *self, int optmask)
|
||||
idnode_serialize0(idnode_t *self, htsmsg_t *list, int optmask)
|
||||
{
|
||||
const idclass_t *idc = self->in_class;
|
||||
const char *uuid, *s;
|
||||
|
@ -908,16 +1179,32 @@ idnode_serialize0(idnode_t *self, int optmask)
|
|||
htsmsg_add_str(m, "caption", s);
|
||||
if ((s = idclass_get_class(idc)))
|
||||
htsmsg_add_str(m, "class", s);
|
||||
if ((s = idclass_get_event(idc)))
|
||||
htsmsg_add_str(m, "event", s);
|
||||
|
||||
htsmsg_add_msg(m, "params", idnode_params(idc, self, optmask));
|
||||
htsmsg_add_msg(m, "params", idnode_params(idc, self, list, optmask));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Notifcation
|
||||
* Notification
|
||||
* *************************************************************************/
|
||||
|
||||
/**
|
||||
* Delayed notification
|
||||
*/
|
||||
static void
|
||||
idnode_notify_delayed ( idnode_t *in, const char *uuid, const char *event )
|
||||
{
|
||||
pthread_mutex_lock(&idnode_mutex);
|
||||
if (!idnode_queue)
|
||||
idnode_queue = htsmsg_create_map();
|
||||
htsmsg_set_str(idnode_queue, uuid, event);
|
||||
pthread_cond_signal(&idnode_cond);
|
||||
pthread_mutex_unlock(&idnode_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update internal event pipes
|
||||
*/
|
||||
|
@ -927,11 +1214,8 @@ idnode_notify_event ( idnode_t *in )
|
|||
const idclass_t *ic = in->in_class;
|
||||
const char *uuid = idnode_uuid_as_str(in);
|
||||
while (ic) {
|
||||
if (ic->ic_event) {
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "uuid", uuid);
|
||||
notify_by_msg(ic->ic_event, m);
|
||||
}
|
||||
if (ic->ic_event)
|
||||
idnode_notify_delayed(in, uuid, ic->ic_event);
|
||||
ic = ic->ic_super;
|
||||
}
|
||||
}
|
||||
|
@ -941,38 +1225,37 @@ idnode_notify_event ( idnode_t *in )
|
|||
*/
|
||||
void
|
||||
idnode_notify
|
||||
(idnode_t *in, const char *chn, int force, int event)
|
||||
(idnode_t *in, int event)
|
||||
{
|
||||
const char *uuid = idnode_uuid_as_str(in);
|
||||
|
||||
if (!tvheadend_running)
|
||||
return;
|
||||
|
||||
/* Forced */
|
||||
if (chn || force) {
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "uuid", uuid);
|
||||
notify_by_msg(chn ?: "idnodeUpdated", m);
|
||||
/* Immediate */
|
||||
if (!event) {
|
||||
|
||||
const idclass_t *ic = in->in_class;
|
||||
|
||||
while (ic) {
|
||||
if (ic->ic_event) {
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "uuid", uuid);
|
||||
notify_by_msg(ic->ic_event, m);
|
||||
}
|
||||
ic = ic->ic_super;
|
||||
}
|
||||
|
||||
/* Rate-limited */
|
||||
} else {
|
||||
pthread_mutex_lock(&idnode_mutex);
|
||||
if (!idnode_queue)
|
||||
idnode_queue = htsmsg_create_map();
|
||||
htsmsg_set_u32(idnode_queue, uuid, 1);
|
||||
pthread_cond_signal(&idnode_cond);
|
||||
pthread_mutex_unlock(&idnode_mutex);
|
||||
}
|
||||
|
||||
/* Send event */
|
||||
if (event)
|
||||
idnode_notify_event(in);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
idnode_notify_simple (void *in)
|
||||
{
|
||||
idnode_notify(in, NULL, 0, 0);
|
||||
idnode_notify(in, 1);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -981,7 +1264,7 @@ idnode_notify_title_changed (void *in)
|
|||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "uuid", idnode_uuid_as_str(in));
|
||||
htsmsg_add_str(m, "text", idnode_get_title(in));
|
||||
notify_by_msg("idnodeUpdated", m);
|
||||
notify_by_msg("title", m);
|
||||
idnode_notify_event(in);
|
||||
}
|
||||
|
||||
|
@ -994,6 +1277,7 @@ idnode_thread ( void *p )
|
|||
idnode_t *node;
|
||||
htsmsg_t *m, *q = NULL;
|
||||
htsmsg_field_t *f;
|
||||
const char *event;
|
||||
|
||||
pthread_mutex_lock(&idnode_mutex);
|
||||
|
||||
|
@ -1012,13 +1296,13 @@ idnode_thread ( void *p )
|
|||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
HTSMSG_FOREACH(f, q) {
|
||||
node = idnode_find(f->hmf_name, NULL);
|
||||
m = htsmsg_create_map();
|
||||
node = idnode_find(f->hmf_name, NULL);
|
||||
event = htsmsg_field_get_str(f);
|
||||
m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "uuid", f->hmf_name);
|
||||
if (node)
|
||||
notify_by_msg("idnodeUpdated", m);
|
||||
else
|
||||
notify_by_msg("idnodeDeleted", m);
|
||||
if (!node)
|
||||
htsmsg_add_u32(m, "removed", 1);
|
||||
notify_by_msg(event, m);
|
||||
}
|
||||
|
||||
/* Finished */
|
||||
|
|
69
src/idnode.h
69
src/idnode.h
|
@ -26,7 +26,7 @@
|
|||
|
||||
#include <regex.h>
|
||||
|
||||
struct htsmsg;
|
||||
struct access;
|
||||
typedef struct idnode idnode_t;
|
||||
|
||||
/*
|
||||
|
@ -39,17 +39,29 @@ typedef struct idnode_set
|
|||
size_t is_count; ///< Current usage of is_array
|
||||
} idnode_set_t;
|
||||
|
||||
/*
|
||||
* Property groups
|
||||
*/
|
||||
typedef struct property_group
|
||||
{
|
||||
const char *name;
|
||||
uint32_t number;
|
||||
uint32_t parent;
|
||||
uint32_t column;
|
||||
} property_group_t;
|
||||
|
||||
/*
|
||||
* Class definition
|
||||
*/
|
||||
typedef struct idclass idclass_t;
|
||||
struct idclass {
|
||||
const struct idclass *ic_super; /// Parent class
|
||||
const char *ic_class; /// Class name
|
||||
const char *ic_caption; /// Class description
|
||||
const char *ic_order; /// Property order (comma separated)
|
||||
const property_t *ic_properties; /// Property list
|
||||
const char *ic_event; /// Events to fire on add/delete/title
|
||||
const struct idclass *ic_super; ///< Parent class
|
||||
const char *ic_class; ///< Class name
|
||||
const char *ic_caption; ///< Class description
|
||||
const char *ic_order; ///< Property order (comma separated)
|
||||
const property_group_t *ic_groups; ///< Groups for visual representation
|
||||
const property_t *ic_properties; ///< Property list
|
||||
const char *ic_event; ///< Events to fire on add/delete/title
|
||||
|
||||
/* Callbacks */
|
||||
idnode_set_t *(*ic_get_childs) (idnode_t *self);
|
||||
|
@ -58,6 +70,7 @@ struct idclass {
|
|||
void (*ic_delete) (idnode_t *self);
|
||||
void (*ic_moveup) (idnode_t *self);
|
||||
void (*ic_movedown) (idnode_t *self);
|
||||
int (*ic_perm) (idnode_t *self, struct access *a, htsmsg_t *msg_to_write);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -87,16 +100,22 @@ typedef struct idnode_filter_ele
|
|||
{
|
||||
LIST_ENTRY(idnode_filter_ele) link; ///< List link
|
||||
|
||||
int checked;
|
||||
char *key; ///< Filter key
|
||||
enum {
|
||||
IF_STR,
|
||||
IF_NUM,
|
||||
IF_DBL,
|
||||
IF_BOOL
|
||||
} type; ///< Filter type
|
||||
union {
|
||||
int b;
|
||||
char *s;
|
||||
int64_t n;
|
||||
struct {
|
||||
int64_t n;
|
||||
int64_t intsplit;
|
||||
} n;
|
||||
double dbl;
|
||||
regex_t re;
|
||||
} u; ///< Filter data
|
||||
enum {
|
||||
|
@ -128,36 +147,50 @@ void idnode_delete (idnode_t *in);
|
|||
void idnode_moveup (idnode_t *in);
|
||||
void idnode_movedown (idnode_t *in);
|
||||
|
||||
void idnode_changed (idnode_t *in);
|
||||
|
||||
void *idnode_find (const char *uuid, const idclass_t *idc);
|
||||
idnode_set_t *idnode_find_all(const idclass_t *idc);
|
||||
|
||||
#define idnode_updated(in) idnode_notify(in, NULL, 0, 0)
|
||||
void idnode_notify
|
||||
(idnode_t *in, const char *chn, int force, int event);
|
||||
|
||||
void idnode_notify (idnode_t *in, int event);
|
||||
void idnode_notify_simple (void *in);
|
||||
void idnode_notify_title_changed (void *in);
|
||||
|
||||
void idclass_register ( const idclass_t *idc );
|
||||
const idclass_t *idclass_find ( const char *name );
|
||||
htsmsg_t *idclass_serialize0 (const idclass_t *idc, int optmask);
|
||||
htsmsg_t *idnode_serialize0 (idnode_t *self, int optmask);
|
||||
void idnode_read0 (idnode_t *self, htsmsg_t *m, int optmask);
|
||||
htsmsg_t *idclass_serialize0 (const idclass_t *idc, htsmsg_t *list, int optmask);
|
||||
htsmsg_t *idnode_serialize0 (idnode_t *self, htsmsg_t *list, int optmask);
|
||||
void idnode_read0 (idnode_t *self, htsmsg_t *m, htsmsg_t *list, int optmask);
|
||||
int idnode_write0 (idnode_t *self, htsmsg_t *m, int optmask, int dosave);
|
||||
|
||||
#define idclass_serialize(idc) idclass_serialize0(idc, 0)
|
||||
#define idnode_serialize(in) idnode_serialize0(in, 0)
|
||||
#define idclass_serialize(idc) idclass_serialize0(idc, NULL, 0)
|
||||
#define idnode_serialize(in) idnode_serialize0(in, NULL, 0)
|
||||
#define idnode_load(in, m) idnode_write0(in, m, PO_NOSAVE, 0)
|
||||
#define idnode_save(in, m) idnode_read0(in, m, PO_NOSAVE | PO_USERAW)
|
||||
#define idnode_save(in, m) idnode_read0(in, m, NULL, PO_NOSAVE | PO_USERAW)
|
||||
#define idnode_update(in, m) idnode_write0(in, m, PO_RDONLY | PO_WRONCE, 1)
|
||||
|
||||
static inline int
|
||||
idnode_perm(idnode_t *self, struct access *a, htsmsg_t *msg_to_write)
|
||||
{
|
||||
if (self->in_class->ic_perm)
|
||||
return self->in_class->ic_perm(self, a, msg_to_write);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *idnode_get_str (idnode_t *self, const char *key );
|
||||
int idnode_get_u32 (idnode_t *self, const char *key, uint32_t *u32);
|
||||
int idnode_get_s64 (idnode_t *self, const char *key, int64_t *s64);
|
||||
int idnode_get_dbl (idnode_t *self, const char *key, double *dbl);
|
||||
int idnode_get_bool(idnode_t *self, const char *key, int *b);
|
||||
int idnode_get_time(idnode_t *self, const char *key, time_t *tm);
|
||||
|
||||
void idnode_filter_add_str
|
||||
(idnode_filter_t *f, const char *k, const char *v, int t);
|
||||
void idnode_filter_add_num
|
||||
(idnode_filter_t *f, const char *k, int64_t s64, int t);
|
||||
(idnode_filter_t *f, const char *k, int64_t s64, int t, int64_t intsplit);
|
||||
void idnode_filter_add_dbl
|
||||
(idnode_filter_t *f, const char *k, double dbl, int t);
|
||||
void idnode_filter_add_bool
|
||||
(idnode_filter_t *f, const char *k, int b, int t);
|
||||
void idnode_filter_clear
|
||||
|
|
|
@ -388,7 +388,7 @@ htsmsg_t *
|
|||
imagecache_get_config ( void )
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
prop_read_values(&imagecache_conf, imagecache_props, m, 0, NULL);
|
||||
prop_read_values(&imagecache_conf, imagecache_props, m, NULL, 0);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <pthread.h>
|
||||
|
||||
struct imagecache_config {
|
||||
int __unused__; // to avoid assert in prop.c (first member should be idnode_t)
|
||||
int enabled;
|
||||
int ignore_sslcert;
|
||||
uint32_t ok_period;
|
||||
|
|
|
@ -43,9 +43,6 @@ tvh_hardware_create0
|
|||
/* Load config */
|
||||
if (conf)
|
||||
idnode_load(&th->th_id, conf);
|
||||
|
||||
/* Update */
|
||||
notify_reload("hardware");
|
||||
|
||||
return o;
|
||||
}
|
||||
|
@ -60,7 +57,6 @@ tvh_hardware_delete ( tvh_hardware_t *th )
|
|||
// TODO
|
||||
LIST_REMOVE(th, th_link);
|
||||
idnode_unlink(&th->th_id);
|
||||
notify_reload("hardware");
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -312,15 +312,15 @@ enum mpegts_mux_epg_flag
|
|||
MM_EPG_DISABLE,
|
||||
MM_EPG_ENABLE,
|
||||
MM_EPG_FORCE,
|
||||
MM_EPG_FORCE_EIT,
|
||||
MM_EPG_FORCE_UK_FREESAT,
|
||||
MM_EPG_FORCE_UK_FREEVIEW,
|
||||
MM_EPG_FORCE_VIASAT_BALTIC,
|
||||
MM_EPG_FORCE_OPENTV_SKY_UK,
|
||||
MM_EPG_FORCE_OPENTV_SKY_ITALIA,
|
||||
MM_EPG_FORCE_OPENTV_SKY_AUSAT,
|
||||
MM_EPG_ONLY_EIT,
|
||||
MM_EPG_ONLY_UK_FREESAT,
|
||||
MM_EPG_ONLY_UK_FREEVIEW,
|
||||
MM_EPG_ONLY_VIASAT_BALTIC,
|
||||
MM_EPG_ONLY_OPENTV_SKY_UK,
|
||||
MM_EPG_ONLY_OPENTV_SKY_ITALIA,
|
||||
MM_EPG_ONLY_OPENTV_SKY_AUSAT,
|
||||
};
|
||||
#define MM_EPG_LAST MM_EPG_FORCE_OPENTV_SKY_AUSAT
|
||||
#define MM_EPG_LAST MM_EPG_ONLY_OPENTV_SKY_AUSAT
|
||||
|
||||
/* Multiplex */
|
||||
struct mpegts_mux
|
||||
|
@ -411,6 +411,7 @@ struct mpegts_mux
|
|||
int mm_enabled;
|
||||
int mm_epg;
|
||||
char *mm_charset;
|
||||
int mm_pmt_06_ac3;
|
||||
};
|
||||
|
||||
/* Service */
|
||||
|
@ -424,6 +425,7 @@ struct mpegts_service
|
|||
|
||||
uint16_t s_dvb_service_id;
|
||||
uint16_t s_dvb_channel_num;
|
||||
uint16_t s_dvb_channel_minor;
|
||||
char *s_dvb_svcname;
|
||||
char *s_dvb_provider;
|
||||
char *s_dvb_cridauth;
|
||||
|
@ -437,7 +439,7 @@ struct mpegts_service
|
|||
*/
|
||||
|
||||
int s_dvb_eit_enable;
|
||||
uint16_t s_dvb_opentv_chnum;
|
||||
uint64_t s_dvb_opentv_chnum;
|
||||
|
||||
/*
|
||||
* Link to carrying multiplex and active adapter
|
||||
|
@ -562,6 +564,9 @@ struct mpegts_input
|
|||
/* Active sources */
|
||||
LIST_HEAD(,mpegts_mux_instance) mi_mux_active;
|
||||
LIST_HEAD(,service) mi_transports;
|
||||
|
||||
mpegts_mux_t **mi_destroyed_muxes;
|
||||
int mi_destroyed_muxes_count;
|
||||
|
||||
/* Table processing */
|
||||
pthread_t mi_table_tid;
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
SKEL_DECLARE(mpegts_table_state_skel, struct mpegts_table_state);
|
||||
|
||||
static int
|
||||
psi_parse_pmt(mpegts_service_t *t, const uint8_t *ptr, int len);
|
||||
psi_parse_pmt(mpegts_mux_t *mux, mpegts_service_t *t, const uint8_t *ptr, int len);
|
||||
|
||||
/* **************************************************************************
|
||||
* Lookup tables
|
||||
|
@ -695,7 +695,7 @@ dvb_pmt_callback
|
|||
tvhdebug("pmt", "sid %04X (%d)", sid, sid);
|
||||
pthread_mutex_lock(&s->s_stream_mutex);
|
||||
had_components = !!TAILQ_FIRST(&s->s_components);
|
||||
r = psi_parse_pmt(s, ptr, len);
|
||||
r = psi_parse_pmt(mt->mt_mux, s, ptr, len);
|
||||
pthread_mutex_unlock(&s->s_stream_mutex);
|
||||
if (r)
|
||||
service_restart((service_t*)s, had_components);
|
||||
|
@ -989,8 +989,7 @@ dvb_sdt_callback
|
|||
|
||||
/* Save details */
|
||||
if (save) {
|
||||
idnode_updated(&s->s_id);
|
||||
s->s_config_save((service_t*)s);
|
||||
idnode_changed(&s->s_id);
|
||||
service_refresh_channel((service_t*)s);
|
||||
}
|
||||
}
|
||||
|
@ -1071,11 +1070,9 @@ atsc_vct_callback
|
|||
tvh_str_set(&s->s_dvb_svcname, chname);
|
||||
save = 1;
|
||||
}
|
||||
if (s->s_dvb_channel_num != maj) {
|
||||
// TODO: ATSC channel numbering is plain weird!
|
||||
// could shift the major (*100 or something) and append
|
||||
// minor, but that'll probably confuse people, as will this!
|
||||
if (s->s_dvb_channel_num != maj || s->s_dvb_channel_minor != min) {
|
||||
s->s_dvb_channel_num = maj;
|
||||
s->s_dvb_channel_minor = min;
|
||||
save = 1;
|
||||
}
|
||||
|
||||
|
@ -1280,7 +1277,7 @@ psi_desc_teletext(mpegts_service_t *t, const uint8_t *ptr, int size,
|
|||
*/
|
||||
static int
|
||||
psi_parse_pmt
|
||||
(mpegts_service_t *t, const uint8_t *ptr, int len)
|
||||
(mpegts_mux_t *mux, mpegts_service_t *t, const uint8_t *ptr, int len)
|
||||
{
|
||||
int ret = 0;
|
||||
uint16_t pcr_pid, pid;
|
||||
|
@ -1377,7 +1374,7 @@ psi_parse_pmt
|
|||
case 0x06:
|
||||
/* 0x06 is Chinese Cable TV AC-3 audio track */
|
||||
/* but mark it so only when no more descriptors exist */
|
||||
if (dllen > 1)
|
||||
if (dllen > 1 || !mux || !mux->mm_pmt_06_ac3)
|
||||
break;
|
||||
/* fall through to SCT_AC3 */
|
||||
case 0x81:
|
||||
|
|
|
@ -72,6 +72,7 @@ const idclass_t linuxdvb_adapter_class =
|
|||
{
|
||||
.ic_class = "linuxdvb_adapter",
|
||||
.ic_caption = "LinuxDVB Adapter",
|
||||
.ic_event = "linuxdvb_adapter",
|
||||
.ic_save = linuxdvb_adapter_class_save,
|
||||
.ic_get_childs = linuxdvb_adapter_class_get_childs,
|
||||
.ic_get_title = linuxdvb_adapter_class_get_title,
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Open things:
|
||||
* - TODO: collision dectection
|
||||
* when a en50494-command wasn't executed succesful, retry.
|
||||
* delay time is easly random, but in standard is special (complicated) way described (cap. 8).
|
||||
* - TODO: collision detection
|
||||
* * compare transport-stream-id from stream with id in config
|
||||
* * check continuity of the pcr-counter
|
||||
* * when one point is given -> retry
|
||||
* * delay time is easily random, but in standard is special (complicated) way described (cap. 8).
|
||||
*/
|
||||
|
||||
#include "tvheadend.h"
|
||||
|
@ -39,7 +41,7 @@
|
|||
#define LINUXDVB_EN50494_NOPIN 256
|
||||
|
||||
#define LINUXDVB_EN50494_FRAME 0xE0
|
||||
/* adresses 0x00, 0x10 and 0x11 are possible */
|
||||
/* addresses 0x00, 0x10 and 0x11 are possible */
|
||||
#define LINUXDVB_EN50494_ADDRESS 0x10
|
||||
|
||||
#define LINUXDVB_EN50494_CMD_NORMAL 0x5A
|
||||
|
@ -162,7 +164,7 @@ linuxdvb_en50494_tune
|
|||
linuxdvb_en50494_t *le = (linuxdvb_en50494_t*) ld;
|
||||
linuxdvb_lnb_t *lnb = sc->lse_lnb;
|
||||
|
||||
/* band & polarisation */
|
||||
/* band & polarization */
|
||||
uint8_t pol = lnb->lnb_pol(lnb, lm);
|
||||
uint8_t band = lnb->lnb_band(lnb, lm);
|
||||
uint32_t freq = lnb->lnb_freq(lnb, lm);
|
||||
|
@ -180,21 +182,29 @@ linuxdvb_en50494_tune
|
|||
/* 2 data fields (16bit) */
|
||||
uint8_t data1, data2;
|
||||
data1 = (le->le_id & 7) << 5; /* 3bit user-band */
|
||||
data1 |= (le->le_position & 1) << 4; /* 1bit position (satelitte A(0)/B(1)) */
|
||||
data1 |= (pol & 1) << 3; /* 1bit polarisation v(0)/h(1) */
|
||||
data1 |= (le->le_position & 1) << 4; /* 1bit position (satellite A(0)/B(1)) */
|
||||
data1 |= (pol & 1) << 3; /* 1bit polarization v(0)/h(1) */
|
||||
data1 |= (band & 1) << 2; /* 1bit band lower(0)/upper(1) */
|
||||
data1 |= (t >> 8) & 3; /* 2bit transponder value bit 1-2 */
|
||||
data2 = t & 0xFF; /* 8bit transponder value bit 3-10 */
|
||||
tvhdebug("en50494",
|
||||
"lnb=%i id=%i freq=%i pin=%i v/h=%i l/u=%i f=%i, data=0x%02X%02X",
|
||||
le->le_position, le->le_id, le->le_frequency, le->le_pin, pol,
|
||||
band, freq, data1, data2);
|
||||
|
||||
pthread_mutex_lock(&linuxdvb_en50494_lock);
|
||||
/* wait until no other thread is setting up switch.
|
||||
* when an other thread was blocking, waiting 20ms.
|
||||
*/
|
||||
if (pthread_mutex_trylock(&linuxdvb_en50494_lock) != 0) {
|
||||
if (pthread_mutex_lock(&linuxdvb_en50494_lock) != 0) {
|
||||
tvherror("en50494","failed to lock for tuning");
|
||||
return -1;
|
||||
}
|
||||
usleep(20000);
|
||||
}
|
||||
|
||||
/* setup en50494 switch */
|
||||
for (i = 0; i <= sc->lse_parent->ls_diseqc_repeats; i++) {
|
||||
/* to avoid repeated collision, wait a random time (5-25ms) */
|
||||
/* to avoid repeated collision, wait a random time 68-118
|
||||
* 67,5 is the typical diseqc-time */
|
||||
if (i != 0) {
|
||||
int ms = rand()%20 + 5;
|
||||
int ms = rand()%50 + 68;
|
||||
usleep(ms*1000);
|
||||
}
|
||||
|
||||
|
@ -207,6 +217,10 @@ linuxdvb_en50494_tune
|
|||
usleep(15000); /* standard: 4ms < x < 22ms */
|
||||
|
||||
/* send tune command (with/without pin) */
|
||||
tvhdebug("en50494",
|
||||
"lnb=%i id=%i freq=%i pin=%i v/h=%i l/u=%i f=%i, data=0x%02X%02X",
|
||||
le->le_position, le->le_id, le->le_frequency, le->le_pin, pol,
|
||||
band, freq, data1, data2);
|
||||
if (le->le_pin != LINUXDVB_EN50494_NOPIN) {
|
||||
ret = linuxdvb_diseqc_send(fd,
|
||||
LINUXDVB_EN50494_FRAME,
|
||||
|
@ -273,7 +287,7 @@ linuxdvb_en50494_create0
|
|||
return NULL;
|
||||
|
||||
if (port > 1) {
|
||||
tvherror("en50494", "only 2 ports/positions are posible. given %i", port);
|
||||
tvherror("en50494", "only 2 ports/positions are possible. given %i", port);
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -289,13 +289,14 @@ linuxdvb_frontend_stop_mux
|
|||
lfe->lfe_ready = 0;
|
||||
lfe->lfe_locked = 0;
|
||||
lfe->lfe_status = 0;
|
||||
assert(lfe->lfe_in_setup == 0);
|
||||
|
||||
/* Ensure it won't happen immediately */
|
||||
gtimer_arm(&lfe->lfe_monitor_timer, linuxdvb_frontend_monitor, lfe, 2);
|
||||
|
||||
if (lfe->lfe_satconf)
|
||||
linuxdvb_satconf_post_stop_mux(lfe->lfe_satconf);
|
||||
|
||||
lfe->lfe_in_setup = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -105,6 +105,10 @@ static int
|
|||
linuxdvb_lnb_standard_tune
|
||||
( linuxdvb_diseqc_t *ld, dvb_mux_t *lm, linuxdvb_satconf_ele_t *ls, int fd )
|
||||
{
|
||||
/* en50494 does not use the voltage tune. this is happend in the switch */
|
||||
if (ls->lse_en50494)
|
||||
return 0;
|
||||
|
||||
int pol = linuxdvb_lnb_standard_pol((linuxdvb_lnb_t*)ld, lm);
|
||||
return linuxdvb_diseqc_set_volt(fd, pol);
|
||||
}
|
||||
|
|
|
@ -211,6 +211,7 @@ const idclass_t linuxdvb_satconf_class =
|
|||
{
|
||||
.ic_class = "linuxdvb_satconf",
|
||||
.ic_caption = "DVB-S Satconf",
|
||||
.ic_event = "linuxdvb_satconf",
|
||||
.ic_get_title = linuxdvb_satconf_class_get_title,
|
||||
.ic_save = linuxdvb_satconf_class_save,
|
||||
.ic_properties = (const property_t[]) {
|
||||
|
@ -632,10 +633,12 @@ linuxdvb_satconf_ele_tune ( linuxdvb_satconf_ele_t *lse )
|
|||
// TODO: really need to understand whether or not we need to pre configure
|
||||
// and/or re-affirm the switch
|
||||
|
||||
/* Disable tone */
|
||||
if (ioctl(lfe->lfe_fe_fd, FE_SET_TONE, SEC_TONE_OFF)) {
|
||||
tvherror("diseqc", "failed to disable tone");
|
||||
return -1;
|
||||
/* Disable tone (en50494 don't use tone) */
|
||||
if (!lse->lse_en50494) {
|
||||
if (ioctl(lfe->lfe_fe_fd, FE_SET_TONE, SEC_TONE_OFF)) {
|
||||
tvherror("diseqc", "failed to disable tone");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Diseqc */
|
||||
|
@ -659,14 +662,16 @@ linuxdvb_satconf_ele_tune ( linuxdvb_satconf_ele_t *lse )
|
|||
&lse->lse_parent->ls_orbital_pos,
|
||||
&lse->lse_parent->ls_orbital_dir);
|
||||
|
||||
/* Set the tone */
|
||||
b = lse->lse_lnb->lnb_band(lse->lse_lnb, lm);
|
||||
tvhtrace("disqec", "set diseqc tone %s", b ? "on" : "off");
|
||||
if (ioctl(lfe->lfe_fe_fd, FE_SET_TONE, b ? SEC_TONE_ON : SEC_TONE_OFF)) {
|
||||
tvherror("diseqc", "failed to set diseqc tone (e=%s)", strerror(errno));
|
||||
return -1;
|
||||
/* Set the tone (en50494 don't use tone) */
|
||||
if (!lse->lse_en50494) {
|
||||
b = lse->lse_lnb->lnb_band(lse->lse_lnb, lm);
|
||||
tvhtrace("disqec", "set diseqc tone %s", b ? "on" : "off");
|
||||
if (ioctl(lfe->lfe_fe_fd, FE_SET_TONE, b ? SEC_TONE_ON : SEC_TONE_OFF)) {
|
||||
tvherror("diseqc", "failed to set diseqc tone (e=%s)", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
usleep(20000); // Allow LNB to settle before tuning
|
||||
}
|
||||
usleep(20000); // Allow LNB to settle before tuning
|
||||
|
||||
/* Frontend */
|
||||
/* use en50494 tuning frequency, if needed (not channel frequency) */
|
||||
|
@ -700,13 +705,16 @@ linuxdvb_satconf_start_mux
|
|||
// Note: basically this ensures the tuning params are acceptable
|
||||
// for the FE, so that if they're not we don't have to wait
|
||||
// for things like rotors and switches
|
||||
// the en50494 have to skip this test
|
||||
if (!lse->lse_lnb)
|
||||
return SM_CODE_TUNING_FAILED;
|
||||
f = lse->lse_lnb->lnb_freq(lse->lse_lnb, lm);
|
||||
if (f == (uint32_t)-1)
|
||||
return SM_CODE_TUNING_FAILED;
|
||||
r = linuxdvb_frontend_tune0(lfe, mmi, f);
|
||||
if (r) return r;
|
||||
if (!lse->lse_en50494) {
|
||||
r = linuxdvb_frontend_tune0(lfe, mmi, f);
|
||||
if (r) return r;
|
||||
}
|
||||
|
||||
/* Diseqc */
|
||||
ls->ls_mmi = mmi;
|
||||
|
@ -1020,6 +1028,7 @@ const idclass_t linuxdvb_satconf_ele_class =
|
|||
{
|
||||
.ic_class = "linuxdvb_satconf_ele",
|
||||
.ic_caption = "Satconf",
|
||||
.ic_event = "linuxdvb_satconf_ele",
|
||||
.ic_get_title = linuxdvb_satconf_ele_class_get_title,
|
||||
.ic_get_childs = linuxdvb_satconf_ele_class_get_childs,
|
||||
.ic_save = linuxdvb_satconf_ele_class_save,
|
||||
|
@ -1205,6 +1214,7 @@ const idclass_t linuxdvb_diseqc_class =
|
|||
{
|
||||
.ic_class = "linuxdvb_diseqc",
|
||||
.ic_caption = "DiseqC",
|
||||
.ic_event = "linuxdvb_diseqc",
|
||||
.ic_get_title = linuxdvb_diseqc_class_get_title,
|
||||
.ic_save = linuxdvb_diseqc_class_save,
|
||||
};
|
||||
|
|
|
@ -142,6 +142,7 @@ const idclass_t mpegts_input_class =
|
|||
{
|
||||
.ic_class = "mpegts_input",
|
||||
.ic_caption = "MPEGTS Input",
|
||||
.ic_event = "mpegts_input",
|
||||
.ic_get_title = mpegts_input_class_get_title,
|
||||
.ic_properties = (const property_t[]){
|
||||
{
|
||||
|
@ -851,6 +852,7 @@ mpegts_input_table_thread ( void *aux )
|
|||
{
|
||||
mpegts_table_feed_t *mtf;
|
||||
mpegts_input_t *mi = aux;
|
||||
int i;
|
||||
|
||||
pthread_mutex_lock(&mi->mi_output_lock);
|
||||
while (mi->mi_running) {
|
||||
|
@ -866,7 +868,18 @@ mpegts_input_table_thread ( void *aux )
|
|||
/* Process */
|
||||
if (mtf->mtf_mux) {
|
||||
pthread_mutex_lock(&global_lock);
|
||||
mpegts_input_table_dispatch(mtf->mtf_mux, mtf->mtf_tsb);
|
||||
if (mi->mi_destroyed_muxes) {
|
||||
for (i = 0; i < mi->mi_destroyed_muxes_count; i++)
|
||||
if (mtf->mtf_mux == mi->mi_destroyed_muxes[i])
|
||||
goto clean;
|
||||
mpegts_input_table_dispatch(mtf->mtf_mux, mtf->mtf_tsb);
|
||||
clean:
|
||||
free(mi->mi_destroyed_muxes);
|
||||
mi->mi_destroyed_muxes = NULL;
|
||||
mi->mi_destroyed_muxes_count = 0;
|
||||
} else {
|
||||
mpegts_input_table_dispatch(mtf->mtf_mux, mtf->mtf_tsb);
|
||||
}
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
}
|
||||
|
||||
|
@ -892,6 +905,8 @@ mpegts_input_flush_mux
|
|||
mpegts_table_feed_t *mtf;
|
||||
mpegts_packet_t *mp;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
// Note: to avoid long delays in here, rather than actually
|
||||
// remove things from the Q, we simply invalidate by clearing
|
||||
// the mux pointer and allow the threads to deal with the deletion
|
||||
|
@ -910,6 +925,10 @@ mpegts_input_flush_mux
|
|||
if (mtf->mtf_mux == mm)
|
||||
mtf->mtf_mux = NULL;
|
||||
}
|
||||
mi->mi_destroyed_muxes = realloc(mi->mi_destroyed_muxes,
|
||||
(mi->mi_destroyed_muxes_count + 1) *
|
||||
sizeof(mpegts_mux_t *));
|
||||
mi->mi_destroyed_muxes[mi->mi_destroyed_muxes_count++] = mm;
|
||||
pthread_mutex_unlock(&mi->mi_output_lock);
|
||||
}
|
||||
|
||||
|
@ -1136,6 +1155,7 @@ mpegts_input_delete ( mpegts_input_t *mi, int delconf )
|
|||
pthread_mutex_destroy(&mi->mi_output_lock);
|
||||
pthread_cond_destroy(&mi->mi_table_cond);
|
||||
free(mi->mi_name);
|
||||
free(mi->mi_destroyed_muxes);
|
||||
free(mi);
|
||||
}
|
||||
|
||||
|
|
|
@ -303,13 +303,13 @@ mpegts_mux_epg_list ( void *o )
|
|||
{ "Disable", MM_EPG_DISABLE },
|
||||
{ "Enable (auto)", MM_EPG_ENABLE },
|
||||
{ "Force (auto)", MM_EPG_FORCE },
|
||||
{ "Force EIT", MM_EPG_FORCE_EIT },
|
||||
{ "Force UK Freesat", MM_EPG_FORCE_UK_FREESAT },
|
||||
{ "Force UK Freeview", MM_EPG_FORCE_UK_FREEVIEW },
|
||||
{ "Force Viasat Baltic", MM_EPG_FORCE_VIASAT_BALTIC },
|
||||
{ "Force OpenTV Sky UK", MM_EPG_FORCE_OPENTV_SKY_UK },
|
||||
{ "Force OpenTV Sky Italia", MM_EPG_FORCE_OPENTV_SKY_ITALIA },
|
||||
{ "Force OpenTV Sky Ausat", MM_EPG_FORCE_OPENTV_SKY_AUSAT },
|
||||
{ "Only EIT", MM_EPG_ONLY_EIT },
|
||||
{ "Only UK Freesat", MM_EPG_ONLY_UK_FREESAT },
|
||||
{ "Only UK Freeview", MM_EPG_ONLY_UK_FREEVIEW },
|
||||
{ "Only Viasat Baltic", MM_EPG_ONLY_VIASAT_BALTIC },
|
||||
{ "Only OpenTV Sky UK", MM_EPG_ONLY_OPENTV_SKY_UK },
|
||||
{ "Only OpenTV Sky Italia", MM_EPG_ONLY_OPENTV_SKY_ITALIA },
|
||||
{ "Only OpenTV Sky Ausat", MM_EPG_ONLY_OPENTV_SKY_AUSAT },
|
||||
};
|
||||
return strtab2htsmsg(tab);
|
||||
}
|
||||
|
@ -406,6 +406,13 @@ const idclass_t mpegts_mux_class =
|
|||
.opts = PO_RDONLY | PO_NOSAVE,
|
||||
.get = mpegts_mux_class_get_num_svc,
|
||||
},
|
||||
{
|
||||
.type = PT_BOOL,
|
||||
.id = "pmt_06_ac3",
|
||||
.name = "PMT Descriptor 0x06 = AC-3",
|
||||
.off = offsetof(mpegts_mux_t, mm_pmt_06_ac3),
|
||||
.opts = PO_ADVANCED,
|
||||
},
|
||||
{}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
static void
|
||||
mpegts_network_scan_notify ( mpegts_mux_t *mm )
|
||||
{
|
||||
idnode_updated(&mm->mm_id);
|
||||
idnode_updated(&mm->mm_network->mn_id);
|
||||
idnode_notify_simple(&mm->mm_id);
|
||||
idnode_notify_simple(&mm->mm_network->mn_id);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <assert.h>
|
||||
|
||||
#include "service.h"
|
||||
#include "channels.h"
|
||||
#include "input.h"
|
||||
#include "settings.h"
|
||||
#include "dvb_charset.h"
|
||||
|
@ -101,6 +102,13 @@ const idclass_t mpegts_service_class =
|
|||
.opts = PO_RDONLY,
|
||||
.off = offsetof(mpegts_service_t, s_dvb_channel_num),
|
||||
},
|
||||
{
|
||||
.type = PT_U16,
|
||||
.id = "lcn_minor",
|
||||
.name = "Local Channel Minor",
|
||||
.opts = PO_RDONLY,
|
||||
.off = offsetof(mpegts_service_t, s_dvb_channel_minor),
|
||||
},
|
||||
{
|
||||
.type = PT_U16,
|
||||
.id = "lcn2",
|
||||
|
@ -364,12 +372,13 @@ mpegts_service_grace_period(service_t *t)
|
|||
/*
|
||||
* Channel number
|
||||
*/
|
||||
static int
|
||||
static int64_t
|
||||
mpegts_service_channel_number ( service_t *s )
|
||||
{
|
||||
int r = ((mpegts_service_t*)s)->s_dvb_channel_num;
|
||||
int r = ((mpegts_service_t*)s)->s_dvb_channel_num * CHANNEL_SPLIT +
|
||||
((mpegts_service_t*)s)->s_dvb_channel_minor;
|
||||
if (r <= 0)
|
||||
r = ((mpegts_service_t*)s)->s_dvb_opentv_chnum;
|
||||
r = ((mpegts_service_t*)s)->s_dvb_opentv_chnum * CHANNEL_SPLIT;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -423,7 +432,9 @@ mpegts_service_create0
|
|||
{
|
||||
int r;
|
||||
char buf[256];
|
||||
service_create0((service_t*)s, class, uuid, S_MPEG_TS, conf);
|
||||
|
||||
if (service_create0((service_t*)s, class, uuid, S_MPEG_TS, conf) == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Create */
|
||||
sbuf_init(&s->s_tsbuf);
|
||||
|
@ -457,8 +468,8 @@ mpegts_service_create0
|
|||
tvhlog(LOG_DEBUG, "mpegts", "%s - add service %04X %s", buf, s->s_dvb_service_id, s->s_dvb_svcname);
|
||||
|
||||
/* Notification */
|
||||
idnode_updated(&mm->mm_id);
|
||||
idnode_updated(&mm->mm_network->mn_id);
|
||||
idnode_notify_simple(&mm->mm_id);
|
||||
idnode_notify_simple(&mm->mm_network->mn_id);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -169,6 +169,7 @@ const idclass_t satip_satconf_class =
|
|||
{
|
||||
.ic_class = "satip_satconf",
|
||||
.ic_caption = "Satconf",
|
||||
.ic_event = "satip_satconf",
|
||||
.ic_get_title = satip_satconf_class_get_title,
|
||||
.ic_save = satip_satconf_class_save,
|
||||
.ic_properties = (const property_t[]) {
|
||||
|
|
|
@ -50,6 +50,8 @@ lang_str_t *lang_str_create ( void )
|
|||
void lang_str_destroy ( lang_str_t *ls )
|
||||
{
|
||||
lang_str_ele_t *e;
|
||||
if (ls == NULL)
|
||||
return;
|
||||
while ((e = RB_FIRST(ls))) {
|
||||
if (e->str) free(e->str);
|
||||
RB_REMOVE(ls, e, link);
|
||||
|
@ -158,38 +160,95 @@ int lang_str_append
|
|||
return _lang_str_add(ls, str, lang, 0, 1);
|
||||
}
|
||||
|
||||
/* Serialize */
|
||||
void lang_str_serialize ( lang_str_t *ls, htsmsg_t *m, const char *f )
|
||||
/* Serialize map */
|
||||
htsmsg_t *lang_str_serialize_map ( lang_str_t *ls )
|
||||
{
|
||||
lang_str_ele_t *e;
|
||||
if (!ls) return;
|
||||
if (!ls) return NULL;
|
||||
htsmsg_t *a = htsmsg_create_map();
|
||||
RB_FOREACH(e, ls, link) {
|
||||
htsmsg_add_str(a, e->lang, e->str);
|
||||
}
|
||||
htsmsg_add_msg(m, f, a);
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Serialize */
|
||||
void lang_str_serialize ( lang_str_t *ls, htsmsg_t *m, const char *f )
|
||||
{
|
||||
if (!ls) return;
|
||||
htsmsg_add_msg(m, f, lang_str_serialize_map(ls));
|
||||
}
|
||||
|
||||
/* De-serialize map */
|
||||
lang_str_t *lang_str_deserialize_map ( htsmsg_t *map )
|
||||
{
|
||||
lang_str_t *ret = lang_str_create();
|
||||
htsmsg_field_t *f;
|
||||
const char *str;
|
||||
|
||||
HTSMSG_FOREACH(f, map) {
|
||||
if ((str = htsmsg_field_get_string(f))) {
|
||||
lang_str_add(ret, str, f->hmf_name, 0);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* De-serialize */
|
||||
lang_str_t *lang_str_deserialize ( htsmsg_t *m, const char *n )
|
||||
{
|
||||
lang_str_t *ret = NULL;
|
||||
htsmsg_t *a;
|
||||
htsmsg_field_t *f;
|
||||
const char *str;
|
||||
|
||||
if ((a = htsmsg_get_map(m, n))) {
|
||||
ret = lang_str_create();
|
||||
HTSMSG_FOREACH(f, a) {
|
||||
if ((str = htsmsg_field_get_string(f))) {
|
||||
lang_str_add(ret, str, f->hmf_name, 0);
|
||||
}
|
||||
}
|
||||
return lang_str_deserialize_map(a);
|
||||
} else if ((str = htsmsg_get_str(m, n))) {
|
||||
ret = lang_str_create();
|
||||
lang_str_t *ret = lang_str_create();
|
||||
lang_str_add(ret, str, NULL, 0);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Compare */
|
||||
int lang_str_compare( lang_str_t *ls1, lang_str_t *ls2 )
|
||||
{
|
||||
lang_str_ele_t *e;
|
||||
const char *s1, *s2;
|
||||
int r;
|
||||
|
||||
if (ls1 == NULL && ls2)
|
||||
return -1;
|
||||
if (ls2 == NULL && ls1)
|
||||
return 1;
|
||||
if (ls1 == ls2)
|
||||
return 0;
|
||||
/* Note: may be optimized to not check languages twice */
|
||||
RB_FOREACH(e, ls1, link) {
|
||||
s1 = lang_str_get(ls1, e->lang);
|
||||
s2 = lang_str_get(ls2, e->lang);
|
||||
if (s1 == NULL && s2 != NULL)
|
||||
return -1;
|
||||
if (s2 == NULL && s1 != NULL)
|
||||
return 1;
|
||||
if (s1 == NULL || s2 == NULL)
|
||||
continue;
|
||||
r = strcmp(s1, s2);
|
||||
if (r) return r;
|
||||
}
|
||||
RB_FOREACH(e, ls2, link) {
|
||||
s1 = lang_str_get(ls1, e->lang);
|
||||
s2 = lang_str_get(ls2, e->lang);
|
||||
if (s1 == NULL && s2 != NULL)
|
||||
return -1;
|
||||
if (s2 == NULL && s1 != NULL)
|
||||
return 1;
|
||||
if (s1 == NULL || s2 == NULL)
|
||||
continue;
|
||||
r = strcmp(s1, s2);
|
||||
if (r) return r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lang_str_done( void )
|
||||
|
|
|
@ -47,11 +47,18 @@ int lang_str_append
|
|||
( lang_str_t *ls, const char *str, const char *lang );
|
||||
|
||||
/* Serialize/Deserialize */
|
||||
htsmsg_t *lang_str_serialize_map
|
||||
( lang_str_t *ls );
|
||||
void lang_str_serialize
|
||||
( lang_str_t *ls, htsmsg_t *msg, const char *f );
|
||||
lang_str_t *lang_str_deserialize_map
|
||||
( htsmsg_t *map );
|
||||
lang_str_t *lang_str_deserialize
|
||||
( htsmsg_t *m, const char *f );
|
||||
|
||||
/* Compare */
|
||||
int lang_str_compare ( lang_str_t *ls1, lang_str_t *ls2 );
|
||||
|
||||
/* Init/Done */
|
||||
void lang_str_done( void );
|
||||
|
||||
|
|
|
@ -484,7 +484,8 @@ main(int argc, char **argv)
|
|||
opt_dump = 0,
|
||||
opt_xspf = 0,
|
||||
opt_dbus = 0,
|
||||
opt_dbus_session = 0;
|
||||
opt_dbus_session = 0,
|
||||
opt_nobackup = 0;
|
||||
const char *opt_config = NULL,
|
||||
*opt_user = NULL,
|
||||
*opt_group = NULL,
|
||||
|
@ -507,6 +508,7 @@ main(int argc, char **argv)
|
|||
|
||||
{ 0, NULL, "Service Configuration", OPT_BOOL, NULL },
|
||||
{ 'c', "config", "Alternate config path", OPT_STR, &opt_config },
|
||||
{ 'B', "nobackup", "Do not backup config tree at upgrade", OPT_BOOL, &opt_nobackup },
|
||||
{ 'f', "fork", "Fork and run as daemon", OPT_BOOL, &opt_fork },
|
||||
{ 'u', "user", "Run as user", OPT_STR, &opt_user },
|
||||
{ 'g', "group", "Run as group", OPT_STR, &opt_group },
|
||||
|
@ -787,7 +789,7 @@ main(int argc, char **argv)
|
|||
/* Initialise configuration */
|
||||
uuid_init();
|
||||
idnode_init();
|
||||
config_init(opt_config);
|
||||
config_init(opt_config, opt_nobackup == 0);
|
||||
|
||||
/**
|
||||
* Initialize subsystems
|
||||
|
@ -821,6 +823,8 @@ main(int argc, char **argv)
|
|||
|
||||
subscription_init();
|
||||
|
||||
dvr_config_init();
|
||||
|
||||
access_init(opt_firstrun, opt_noacl);
|
||||
|
||||
#if ENABLE_TIMESHIFT
|
||||
|
|
|
@ -21,9 +21,6 @@
|
|||
|
||||
#include "htsmsg.h"
|
||||
|
||||
#define MC_REWRITE_PAT 0x0001
|
||||
#define MC_REWRITE_PMT 0x0002
|
||||
|
||||
#define MC_IS_EOS_ERROR(e) ((e) == EPIPE || (e) == ECONNRESET)
|
||||
|
||||
typedef enum {
|
||||
|
@ -47,8 +44,9 @@ typedef enum {
|
|||
|
||||
/* Muxer configuration used when creating a muxer. */
|
||||
typedef struct muxer_config {
|
||||
int m_flags;
|
||||
muxer_cache_type_t m_cache;
|
||||
int m_rewrite_pat;
|
||||
int m_rewrite_pmt;
|
||||
int m_cache;
|
||||
|
||||
/*
|
||||
* directory_permissions should really be in dvr.h as it's not really needed for the muxer
|
||||
|
|
|
@ -316,7 +316,7 @@ pass_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss)
|
|||
pm->pm_pmt_pid = ss->ss_pmt_pid;
|
||||
pm->pm_service_id = ss->ss_service_id;
|
||||
|
||||
if (pm->m_config.m_flags & MC_REWRITE_PMT) {
|
||||
if (pm->m_config.m_rewrite_pmt) {
|
||||
pm->pm_pmt = realloc(pm->pm_pmt, 188);
|
||||
memset(pm->pm_pmt, 0xff, 188);
|
||||
pm->pm_pmt[0] = 0x47;
|
||||
|
@ -433,16 +433,15 @@ pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb)
|
|||
size_t len = pb->pb_size;
|
||||
|
||||
/* Rewrite PAT/PMT in operation */
|
||||
if (pm->m_config.m_flags & (MC_REWRITE_PAT | MC_REWRITE_PMT)) {
|
||||
if (pm->m_config.m_rewrite_pat || pm->m_config.m_rewrite_pmt) {
|
||||
tsb = pb->pb_data;
|
||||
len = 0;
|
||||
while (tsb < pb->pb_data + pb->pb_size) {
|
||||
int pid = (tsb[1] & 0x1f) << 8 | tsb[2];
|
||||
|
||||
/* Process */
|
||||
if ( ((pm->m_config.m_flags & MC_REWRITE_PAT) && (pid == 0)) ||
|
||||
((pm->m_config.m_flags & MC_REWRITE_PMT) &&
|
||||
(pid == pm->pm_pmt_pid)) ) {
|
||||
if ( (pm->m_config.m_rewrite_pat && (pid == 0)) ||
|
||||
(pm->m_config.m_rewrite_pmt && (pid == pm->pm_pmt_pid)) ) {
|
||||
|
||||
/* Flush */
|
||||
if (len)
|
||||
|
@ -458,7 +457,7 @@ pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb)
|
|||
e = pass_muxer_rewrite_pat(pm, tmp);
|
||||
if (e < 0) {
|
||||
tvherror("pass", "PAT rewrite failed, disabling");
|
||||
pm->m_config.m_flags &= ~MC_REWRITE_PAT;
|
||||
pm->m_config.m_rewrite_pat = 0;
|
||||
}
|
||||
if (e)
|
||||
pass_muxer_write(m, tmp, 188);
|
||||
|
|
|
@ -652,6 +652,7 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
|||
htsbuf_queue_t *q = htsbuf_queue_alloc(0);
|
||||
char datestr[64], ctype[100];
|
||||
const epg_genre_t *eg = NULL;
|
||||
epg_genre_t eg0;
|
||||
struct tm tm;
|
||||
localtime_r(de ? &de->de_start : &ebc->start, &tm);
|
||||
epg_episode_t *ee = NULL;
|
||||
|
@ -677,8 +678,10 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
|||
|
||||
addtag(q, build_tag_string("ORIGINAL_MEDIA_TYPE", "TV", NULL, 0, NULL));
|
||||
|
||||
if(de && de->de_content_type.code) {
|
||||
eg = &de->de_content_type;
|
||||
if(de && de->de_content_type) {
|
||||
memset(&eg0, 0, sizeof(eg0));
|
||||
eg0.code = de->de_content_type;
|
||||
eg = &eg0;
|
||||
} else if (ee) {
|
||||
eg = LIST_FIRST(&ee->genre);
|
||||
}
|
||||
|
|
368
src/prop.c
368
src/prop.c
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "tvheadend.h"
|
||||
#include "prop.h"
|
||||
#include "lang_str.h"
|
||||
|
||||
/* **************************************************************************
|
||||
* Utilities
|
||||
|
@ -31,12 +32,16 @@
|
|||
*
|
||||
*/
|
||||
const static struct strtab typetab[] = {
|
||||
{ "bool", PT_BOOL },
|
||||
{ "int", PT_INT },
|
||||
{ "str", PT_STR },
|
||||
{ "u16", PT_U16 },
|
||||
{ "u32", PT_U32 },
|
||||
{ "dbl", PT_DBL },
|
||||
{ "bool", PT_BOOL },
|
||||
{ "int", PT_INT },
|
||||
{ "str", PT_STR },
|
||||
{ "u16", PT_U16 },
|
||||
{ "u32", PT_U32 },
|
||||
{ "s64", PT_S64 },
|
||||
{ "dbl", PT_DBL },
|
||||
{ "time", PT_TIME },
|
||||
{ "langstr", PT_LANGSTR },
|
||||
{ "perm", PT_PERM },
|
||||
};
|
||||
|
||||
|
||||
|
@ -71,6 +76,7 @@ prop_write_values
|
|||
int64_t s64;
|
||||
uint32_t u32;
|
||||
uint16_t u16;
|
||||
time_t tm;
|
||||
#define PROP_UPDATE(v, t)\
|
||||
new = &v;\
|
||||
if (!p->set && (*((t*)cur) != *((t*)new))) {\
|
||||
|
@ -81,13 +87,18 @@ prop_write_values
|
|||
if (!pl) return 0;
|
||||
|
||||
for (p = pl; p->id; p++) {
|
||||
|
||||
if (p->type == PT_NONE) continue;
|
||||
|
||||
f = htsmsg_field_find(m, p->id);
|
||||
if (!f) continue;
|
||||
|
||||
/* Ignore */
|
||||
if(p->opts & optmask) continue;
|
||||
u32 = p->get_opts ? p->get_opts(obj) : p->opts;
|
||||
if(u32 & optmask) continue;
|
||||
|
||||
/* Sanity check */
|
||||
assert(p->set || p->off);
|
||||
|
||||
/* Write */
|
||||
save = 0;
|
||||
|
@ -122,11 +133,35 @@ prop_write_values
|
|||
break;
|
||||
}
|
||||
case PT_U32: {
|
||||
if (htsmsg_field_get_u32(f, &u32))
|
||||
continue;
|
||||
if (p->intsplit) {
|
||||
char *s;
|
||||
if (!(new = htsmsg_field_get_str(f)))
|
||||
continue;
|
||||
u32 = atol(new) * p->intsplit;
|
||||
if ((s = strchr(new, '.')) != NULL)
|
||||
u32 += (atol(s + 1) % p->intsplit);
|
||||
} else {
|
||||
if (htsmsg_field_get_u32(f, &u32))
|
||||
continue;
|
||||
}
|
||||
PROP_UPDATE(u32, uint32_t);
|
||||
break;
|
||||
}
|
||||
case PT_S64: {
|
||||
if (p->intsplit) {
|
||||
char *s;
|
||||
if (!(new = htsmsg_field_get_str(f)))
|
||||
continue;
|
||||
s64 = (int64_t)atol(new) * p->intsplit;
|
||||
if ((s = strchr(new, '.')) != NULL)
|
||||
s64 += (atol(s + 1) % p->intsplit);
|
||||
} else {
|
||||
if (htsmsg_field_get_s64(f, &s64))
|
||||
continue;
|
||||
}
|
||||
PROP_UPDATE(s64, int64_t);
|
||||
break;
|
||||
}
|
||||
case PT_DBL: {
|
||||
if (htsmsg_field_get_dbl(f, &dbl))
|
||||
continue;
|
||||
|
@ -144,6 +179,38 @@ prop_write_values
|
|||
}
|
||||
break;
|
||||
}
|
||||
case PT_TIME: {
|
||||
if (htsmsg_field_get_s64(f, &s64))
|
||||
continue;
|
||||
tm = s64;
|
||||
PROP_UPDATE(tm, time_t);
|
||||
break;
|
||||
}
|
||||
case PT_LANGSTR: {
|
||||
lang_str_t **lstr1 = cur;
|
||||
lang_str_t *lstr2;
|
||||
new = htsmsg_field_get_map(f);
|
||||
if (!new)
|
||||
continue;
|
||||
if (!p->set) {
|
||||
lstr2 = lang_str_deserialize_map((htsmsg_t *)new);
|
||||
if (lang_str_compare(*lstr1, lstr2)) {
|
||||
lang_str_destroy(*lstr1);
|
||||
*lstr1 = lstr2;
|
||||
save = 1;
|
||||
} else {
|
||||
lang_str_destroy(lstr2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PT_PERM: {
|
||||
if (!(new = htsmsg_field_get_str(f)))
|
||||
continue;
|
||||
u32 = (int)strtol(new,NULL,0);
|
||||
PROP_UPDATE(u32, uint32_t);
|
||||
break;
|
||||
}
|
||||
case PT_NONE:
|
||||
break;
|
||||
}
|
||||
|
@ -175,19 +242,20 @@ prop_write_values
|
|||
*/
|
||||
static void
|
||||
prop_read_value
|
||||
(void *obj, const property_t *p, htsmsg_t *m, const char *name,
|
||||
int optmask, htsmsg_t *inc)
|
||||
(void *obj, const property_t *p, htsmsg_t *m, const char *name, int optmask)
|
||||
{
|
||||
const char *s;
|
||||
const void *val = obj + p->off;
|
||||
uint32_t u32;
|
||||
char buf[24];
|
||||
|
||||
/* Ignore */
|
||||
if (p->opts & optmask) return;
|
||||
u32 = p->get_opts ? p->get_opts(obj) : p->opts;
|
||||
if (u32 & optmask) return;
|
||||
if (p->type == PT_NONE) return;
|
||||
|
||||
/* Ignore */
|
||||
if (inc && !htsmsg_get_u32_or_default(inc, p->id, 0))
|
||||
return;
|
||||
/* Sanity check */
|
||||
assert(p->get || p->off);
|
||||
|
||||
/* Get method */
|
||||
if (!(optmask & PO_USERAW) || !p->off)
|
||||
|
@ -207,12 +275,33 @@ prop_read_value
|
|||
case PT_INT:
|
||||
htsmsg_add_s64(m, name, *(int *)val);
|
||||
break;
|
||||
case PT_U32:
|
||||
htsmsg_add_u32(m, name, *(uint32_t *)val);
|
||||
break;
|
||||
case PT_U16:
|
||||
htsmsg_add_u32(m, name, *(uint16_t *)val);
|
||||
break;
|
||||
case PT_U32:
|
||||
if (p->intsplit) {
|
||||
uint32_t maj = *(int64_t *)val / p->intsplit;
|
||||
uint32_t min = *(int64_t *)val % p->intsplit;
|
||||
if (min) {
|
||||
snprintf(buf, sizeof(buf), "%u.%u", (unsigned int)maj, (unsigned int)min);
|
||||
htsmsg_add_str(m, name, buf);
|
||||
} else
|
||||
htsmsg_add_s64(m, name, maj);
|
||||
} else
|
||||
htsmsg_add_u32(m, name, *(uint32_t *)val);
|
||||
break;
|
||||
case PT_S64:
|
||||
if (p->intsplit) {
|
||||
int64_t maj = *(int64_t *)val / p->intsplit;
|
||||
int64_t min = *(int64_t *)val % p->intsplit;
|
||||
if (min) {
|
||||
snprintf(buf, sizeof(buf), "%lu.%lu", (unsigned long)maj, (unsigned long)min);
|
||||
htsmsg_add_str(m, name, buf);
|
||||
} else
|
||||
htsmsg_add_s64(m, name, maj);
|
||||
} else
|
||||
htsmsg_add_s64(m, name, *(int64_t *)val);
|
||||
break;
|
||||
case PT_STR:
|
||||
if ((s = *(const char **)val))
|
||||
htsmsg_add_str(m, name, s);
|
||||
|
@ -220,6 +309,16 @@ prop_read_value
|
|||
case PT_DBL:
|
||||
htsmsg_add_dbl(m, name, *(double*)val);
|
||||
break;
|
||||
case PT_TIME:
|
||||
htsmsg_add_s64(m, name, *(time_t *)val);
|
||||
break;
|
||||
case PT_LANGSTR:
|
||||
lang_str_serialize(*(lang_str_t **)val, m, name);
|
||||
break;
|
||||
case PT_PERM:
|
||||
snprintf(buf, sizeof(buf), "%04o", *(uint32_t *)val);
|
||||
htsmsg_add_str(m, name, buf);
|
||||
break;
|
||||
case PT_NONE:
|
||||
break;
|
||||
}
|
||||
|
@ -231,12 +330,141 @@ prop_read_value
|
|||
*/
|
||||
void
|
||||
prop_read_values
|
||||
(void *obj, const property_t *pl, htsmsg_t *m, int optmask, htsmsg_t *inc)
|
||||
(void *obj, const property_t *pl, htsmsg_t *m, htsmsg_t *list, int optmask)
|
||||
{
|
||||
if(pl == NULL)
|
||||
return;
|
||||
for (; pl->id; pl++)
|
||||
prop_read_value(obj, pl, m, pl->id, optmask, inc);
|
||||
|
||||
if(list == NULL) {
|
||||
for (; pl->id; pl++)
|
||||
prop_read_value(obj, pl, m, pl->id, optmask);
|
||||
} else {
|
||||
const property_t *p;
|
||||
htsmsg_field_t *f;
|
||||
int b;
|
||||
HTSMSG_FOREACH(f, list) {
|
||||
if (!htsmsg_field_get_bool(f, &b) && b > 0) {
|
||||
p = prop_find(pl, f->hmf_name);
|
||||
if (p)
|
||||
prop_read_value(obj, p, m, p->id, optmask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
prop_serialize_value
|
||||
(void *obj, const property_t *pl, htsmsg_t *msg, int optmask)
|
||||
{
|
||||
htsmsg_field_t *f;
|
||||
char buf[16];
|
||||
uint32_t opts;
|
||||
|
||||
/* Remove parent */
|
||||
// TODO: this is really horrible and inefficient!
|
||||
HTSMSG_FOREACH(f, msg) {
|
||||
htsmsg_t *t = htsmsg_field_get_map(f);
|
||||
const char *str;
|
||||
if (t && (str = htsmsg_get_str(t, "id"))) {
|
||||
if (!strcmp(str, pl->id)) {
|
||||
htsmsg_field_destroy(msg, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
|
||||
/* ID / type */
|
||||
htsmsg_add_str(m, "id", pl->id);
|
||||
htsmsg_add_str(m, "type", val2str(pl->type, typetab) ?: "none");
|
||||
|
||||
/* Skip - special blocker */
|
||||
if (pl->type == PT_NONE) {
|
||||
htsmsg_add_msg(msg, NULL, m);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Metadata */
|
||||
htsmsg_add_str(m, "caption", pl->name);
|
||||
if (pl->islist)
|
||||
htsmsg_add_u32(m, "list", 1);
|
||||
|
||||
/* Default */
|
||||
// TODO: currently no support for list defaults
|
||||
switch (pl->type) {
|
||||
case PT_BOOL:
|
||||
htsmsg_add_bool(m, "default", pl->def.i);
|
||||
break;
|
||||
case PT_INT:
|
||||
htsmsg_add_s32(m, "default", pl->def.i);
|
||||
break;
|
||||
case PT_U16:
|
||||
htsmsg_add_u32(m, "default", pl->def.u16);
|
||||
break;
|
||||
case PT_U32:
|
||||
htsmsg_add_u32(m, "default", pl->def.u32);
|
||||
break;
|
||||
case PT_S64:
|
||||
htsmsg_add_s64(m, "default", pl->def.s64);
|
||||
break;
|
||||
case PT_DBL:
|
||||
htsmsg_add_dbl(m, "default", pl->def.d);
|
||||
break;
|
||||
case PT_STR:
|
||||
htsmsg_add_str(m, "default", pl->def.s ?: "");
|
||||
break;
|
||||
case PT_TIME:
|
||||
htsmsg_add_s64(m, "default", pl->def.tm);
|
||||
break;
|
||||
case PT_LANGSTR:
|
||||
/* TODO? */
|
||||
break;
|
||||
case PT_PERM:
|
||||
snprintf(buf, sizeof(buf), "%04o", pl->def.u32);
|
||||
htsmsg_add_str(m, "default", buf);
|
||||
break;
|
||||
case PT_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Options */
|
||||
opts = pl->get_opts ? pl->get_opts(obj) : pl->opts;
|
||||
if (opts & PO_RDONLY)
|
||||
htsmsg_add_bool(m, "rdonly", 1);
|
||||
if (opts & PO_NOSAVE)
|
||||
htsmsg_add_bool(m, "nosave", 1);
|
||||
if (opts & PO_WRONCE)
|
||||
htsmsg_add_bool(m, "wronce", 1);
|
||||
if (opts & PO_ADVANCED)
|
||||
htsmsg_add_bool(m, "advanced", 1);
|
||||
if (opts & PO_HIDDEN)
|
||||
htsmsg_add_bool(m, "hidden", 1);
|
||||
if (opts & PO_PASSWORD)
|
||||
htsmsg_add_bool(m, "password", 1);
|
||||
if (opts & PO_DURATION)
|
||||
htsmsg_add_bool(m, "duration", 1);
|
||||
|
||||
/* Enum list */
|
||||
if (pl->list)
|
||||
htsmsg_add_msg(m, "enum", pl->list(obj));
|
||||
|
||||
/* Visual group */
|
||||
if (pl->group)
|
||||
htsmsg_add_u32(m, "group", pl->group);
|
||||
|
||||
/* Split integer value */
|
||||
if (pl->intsplit)
|
||||
htsmsg_add_u32(m, "intsplit", pl->intsplit);
|
||||
|
||||
/* Data */
|
||||
if (obj)
|
||||
prop_read_value(obj, pl, m, "value", optmask);
|
||||
|
||||
htsmsg_add_msg(msg, NULL, m);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,97 +472,25 @@ prop_read_values
|
|||
*/
|
||||
void
|
||||
prop_serialize
|
||||
(void *obj, const property_t *pl, htsmsg_t *msg, int optmask, htsmsg_t *inc)
|
||||
(void *obj, const property_t *pl, htsmsg_t *msg, htsmsg_t *list, int optmask)
|
||||
{
|
||||
htsmsg_field_t *f;
|
||||
|
||||
if(pl == NULL)
|
||||
return;
|
||||
|
||||
for(; pl->id; pl++) {
|
||||
|
||||
/* Remove parent */
|
||||
// TODO: this is really horrible and inefficient!
|
||||
HTSMSG_FOREACH(f, msg) {
|
||||
htsmsg_t *t = htsmsg_field_get_map(f);
|
||||
const char *str;
|
||||
if (t && (str = htsmsg_get_str(t, "id"))) {
|
||||
if (!strcmp(str, pl->id)) {
|
||||
htsmsg_field_destroy(msg, f);
|
||||
break;
|
||||
}
|
||||
if(list == NULL) {
|
||||
for (; pl->id; pl++)
|
||||
prop_serialize_value(obj, pl, msg, optmask);
|
||||
} else {
|
||||
const property_t *p;
|
||||
htsmsg_field_t *f;
|
||||
int b;
|
||||
HTSMSG_FOREACH(f, list) {
|
||||
if (!htsmsg_field_get_bool(f, &b) && b > 0) {
|
||||
p = prop_find(pl, f->hmf_name);
|
||||
if (p)
|
||||
prop_serialize_value(obj, p, msg, optmask);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ignore */
|
||||
if (inc && !htsmsg_get_u32_or_default(inc, pl->id, 0))
|
||||
continue;
|
||||
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
|
||||
/* ID / type */
|
||||
htsmsg_add_str(m, "id", pl->id);
|
||||
htsmsg_add_str(m, "type", val2str(pl->type, typetab) ?: "none");
|
||||
|
||||
/* Skip - special blocker */
|
||||
if (pl->type == PT_NONE) {
|
||||
htsmsg_add_msg(msg, NULL, m);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Metadata */
|
||||
htsmsg_add_str(m, "caption", pl->name);
|
||||
if (pl->islist)
|
||||
htsmsg_add_u32(m, "list", 1);
|
||||
|
||||
/* Default */
|
||||
// TODO: currently no support for list defaults
|
||||
switch (pl->type) {
|
||||
case PT_BOOL:
|
||||
htsmsg_add_bool(m, "default", pl->def.i);
|
||||
break;
|
||||
case PT_INT:
|
||||
htsmsg_add_s32(m, "default", pl->def.i);
|
||||
break;
|
||||
case PT_U16:
|
||||
htsmsg_add_u32(m, "default", pl->def.u16);
|
||||
break;
|
||||
case PT_U32:
|
||||
htsmsg_add_u32(m, "default", pl->def.u32);
|
||||
break;
|
||||
case PT_DBL:
|
||||
htsmsg_add_dbl(m, "default", pl->def.d);
|
||||
break;
|
||||
case PT_STR:
|
||||
htsmsg_add_str(m, "default", pl->def.s ?: "");
|
||||
break;
|
||||
case PT_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Options */
|
||||
if (pl->opts & PO_RDONLY)
|
||||
htsmsg_add_bool(m, "rdonly", 1);
|
||||
if (pl->opts & PO_NOSAVE)
|
||||
htsmsg_add_bool(m, "nosave", 1);
|
||||
if (pl->opts & PO_WRONCE)
|
||||
htsmsg_add_bool(m, "wronce", 1);
|
||||
if (pl->opts & PO_ADVANCED)
|
||||
htsmsg_add_bool(m, "advanced", 1);
|
||||
if (pl->opts & PO_HIDDEN)
|
||||
htsmsg_add_bool(m, "hidden", 1);
|
||||
if (pl->opts & PO_PASSWORD)
|
||||
htsmsg_add_bool(m, "password", 1);
|
||||
|
||||
/* Enum list */
|
||||
if (pl->list)
|
||||
htsmsg_add_msg(m, "enum", pl->list(obj));
|
||||
|
||||
/* Data */
|
||||
if (obj)
|
||||
prop_read_value(obj, pl, m, "value", optmask, NULL);
|
||||
|
||||
htsmsg_add_msg(msg, NULL, m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
42
src/prop.h
42
src/prop.h
|
@ -34,21 +34,26 @@ typedef enum {
|
|||
PT_INT,
|
||||
PT_U16,
|
||||
PT_U32,
|
||||
PT_S64,
|
||||
PT_DBL,
|
||||
PT_TIME,
|
||||
PT_LANGSTR,
|
||||
PT_PERM, // like PT_U32 but with the special save
|
||||
} prop_type_t;
|
||||
|
||||
/*
|
||||
* Property options
|
||||
*/
|
||||
#define PO_NONE 0x00
|
||||
#define PO_RDONLY 0x01 // Property is read-only
|
||||
#define PO_NOSAVE 0x02 // Property is transient (not saved)
|
||||
#define PO_WRONCE 0x04 // Property is write-once (i.e. on creation)
|
||||
#define PO_ADVANCED 0x08 // Property is advanced
|
||||
#define PO_HIDDEN 0x10 // Property is hidden (by default)
|
||||
#define PO_USERAW 0x20 // Only save the RAW (off) value if it exists
|
||||
#define PO_SORTKEY 0x40 // Sort using key (not display value)
|
||||
#define PO_PASSWORD 0x80 // String is a password
|
||||
#define PO_NONE 0x0000
|
||||
#define PO_RDONLY 0x0001 // Property is read-only
|
||||
#define PO_NOSAVE 0x0002 // Property is transient (not saved)
|
||||
#define PO_WRONCE 0x0004 // Property is write-once (i.e. on creation)
|
||||
#define PO_ADVANCED 0x0008 // Property is advanced
|
||||
#define PO_HIDDEN 0x0010 // Property is hidden (by default)
|
||||
#define PO_USERAW 0x0020 // Only save the RAW (off) value if it exists
|
||||
#define PO_SORTKEY 0x0040 // Sort using key (not display value)
|
||||
#define PO_PASSWORD 0x0080 // String is a password
|
||||
#define PO_DURATION 0x0100 // For PT_TIME - differentiate between duration and datetime
|
||||
|
||||
/*
|
||||
* Property definition
|
||||
|
@ -57,9 +62,11 @@ typedef struct property {
|
|||
const char *id; ///< Property Key
|
||||
const char *name; ///< Textual description
|
||||
prop_type_t type; ///< Type
|
||||
int islist; ///< Is a list
|
||||
uint8_t islist; ///< Is a list
|
||||
uint8_t group; ///< Visual group ID (like ExtJS FieldSet)
|
||||
size_t off; ///< Offset into object
|
||||
int opts; ///< Options
|
||||
uint32_t opts; ///< Options
|
||||
uint32_t intsplit; ///< integer/remainder boundary
|
||||
|
||||
/* String based processing */
|
||||
const void *(*get) (void *ptr);
|
||||
|
@ -72,12 +79,17 @@ typedef struct property {
|
|||
/* Default (for UI) */
|
||||
union {
|
||||
int i; // PT_BOOL/PT_INT
|
||||
const char *s; // PR_STR
|
||||
const char *s; // PT_STR
|
||||
uint16_t u16; // PT_U16
|
||||
uint32_t u32; // PR_U32
|
||||
uint32_t u32; // PT_U32
|
||||
int64_t s64; // PT_S64
|
||||
double d; // PT_DBL
|
||||
time_t tm; // PT_TIME
|
||||
} def;
|
||||
|
||||
/* Extended options */
|
||||
uint32_t (*get_opts) (void *ptr);
|
||||
|
||||
/* Notification callback */
|
||||
void (*notify) (void *ptr);
|
||||
|
||||
|
@ -89,10 +101,10 @@ int prop_write_values
|
|||
(void *obj, const property_t *pl, htsmsg_t *m, int optmask, htsmsg_t *updated);
|
||||
|
||||
void prop_read_values
|
||||
(void *obj, const property_t *pl, htsmsg_t *m, int optmask, htsmsg_t *inc);
|
||||
(void *obj, const property_t *pl, htsmsg_t *m, htsmsg_t *list, int optmask);
|
||||
|
||||
void prop_serialize
|
||||
(void *obj, const property_t *pl, htsmsg_t *m, int optmask, htsmsg_t *inc);
|
||||
(void *obj, const property_t *pl, htsmsg_t *m, htsmsg_t *list, int optmask);
|
||||
|
||||
#endif /* __TVH_PROP_H__ */
|
||||
|
||||
|
|
|
@ -115,13 +115,10 @@ static htsmsg_t *
|
|||
service_class_channel_enum
|
||||
( void *obj )
|
||||
{
|
||||
htsmsg_t *p, *m = htsmsg_create_map();
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "type", "api");
|
||||
htsmsg_add_str(m, "uri", "channel/list");
|
||||
htsmsg_add_str(m, "event", "channel");
|
||||
p = htsmsg_create_map();
|
||||
htsmsg_add_u32(p, "enum", 1);
|
||||
htsmsg_add_msg(m, "params", p);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
@ -171,6 +168,7 @@ service_class_caid_get ( void *obj )
|
|||
const idclass_t service_class = {
|
||||
.ic_class = "service",
|
||||
.ic_caption = "Service",
|
||||
.ic_event = "service",
|
||||
.ic_save = service_class_save,
|
||||
.ic_get_title = service_class_get_title,
|
||||
.ic_properties = (const property_t[]){
|
||||
|
@ -805,7 +803,7 @@ service_destroy(service_t *t, int delconf)
|
|||
service_unref(t);
|
||||
}
|
||||
|
||||
static int
|
||||
static int64_t
|
||||
service_channel_number ( service_t *s )
|
||||
{
|
||||
return 0;
|
||||
|
@ -1488,7 +1486,6 @@ service_instance_add(service_instance_list_t *sil,
|
|||
si->si_s = s;
|
||||
service_ref(s);
|
||||
si->si_instance = instance;
|
||||
si->si_weight = weight;
|
||||
} else {
|
||||
si->si_mark = 0;
|
||||
if(si->si_prio == prio && si->si_weight == weight)
|
||||
|
@ -1570,7 +1567,7 @@ service_get_full_channel_name ( service_t *s )
|
|||
/*
|
||||
* Get number for service
|
||||
*/
|
||||
int
|
||||
int64_t
|
||||
service_get_channel_number ( service_t *s )
|
||||
{
|
||||
if (s->s_channel_number) return s->s_channel_number(s);
|
||||
|
|
|
@ -292,7 +292,7 @@ typedef struct service {
|
|||
/**
|
||||
* Channel info
|
||||
*/
|
||||
int (*s_channel_number) (struct service *);
|
||||
int64_t (*s_channel_number) (struct service *);
|
||||
const char *(*s_channel_name) (struct service *);
|
||||
const char *(*s_provider_name) (struct service *);
|
||||
|
||||
|
@ -554,6 +554,6 @@ void sort_elementary_streams(service_t *t);
|
|||
|
||||
const char *service_get_channel_name (service_t *s);
|
||||
const char *service_get_full_channel_name (service_t *s);
|
||||
int service_get_channel_number (service_t *s);
|
||||
int64_t service_get_channel_number (service_t *s);
|
||||
|
||||
#endif // SERVICE_H__
|
||||
|
|
|
@ -184,21 +184,22 @@ hts_settings_save(htsmsg_t *record, const char *pathfmt, ...)
|
|||
static htsmsg_t *
|
||||
hts_settings_load_one(const char *filename)
|
||||
{
|
||||
ssize_t n;
|
||||
ssize_t n, size;
|
||||
char *mem;
|
||||
fb_file *fp;
|
||||
htsmsg_t *r = NULL;
|
||||
|
||||
/* Open */
|
||||
if (!(fp = fb_open(filename, 1, 0))) return NULL;
|
||||
size = fb_size(fp);
|
||||
|
||||
/* Load data */
|
||||
mem = malloc(fb_size(fp)+1);
|
||||
n = fb_read(fp, mem, fb_size(fp));
|
||||
mem = malloc(size+1);
|
||||
n = fb_read(fp, mem, size);
|
||||
if (n >= 0) mem[n] = 0;
|
||||
|
||||
/* Decode */
|
||||
if(n == fb_size(fp))
|
||||
if(n == size)
|
||||
r = htsmsg_json_deserialize(mem);
|
||||
|
||||
/* Close */
|
||||
|
|
78
src/spawn.c
78
src/spawn.c
|
@ -84,50 +84,56 @@ find_exec ( const char *name, char *out, size_t len )
|
|||
}
|
||||
|
||||
/**
|
||||
* The reaper is called once a second to finish of any pending spawns
|
||||
* Reap one child
|
||||
*/
|
||||
void
|
||||
spawn_reaper(void)
|
||||
int
|
||||
spawn_reap(char *stxt, size_t stxtlen)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
char txt[100];
|
||||
int status, res;
|
||||
spawn_t *s;
|
||||
|
||||
while(1) {
|
||||
pid = waitpid(-1, &status, WNOHANG);
|
||||
if(pid < 1)
|
||||
pid = waitpid(-1, &status, WNOHANG);
|
||||
if(pid < 1)
|
||||
return -EAGAIN;
|
||||
|
||||
pthread_mutex_lock(&spawn_mutex);
|
||||
LIST_FOREACH(s, &spawns, link)
|
||||
if(s->pid == pid)
|
||||
break;
|
||||
|
||||
pthread_mutex_lock(&spawn_mutex);
|
||||
LIST_FOREACH(s, &spawns, link)
|
||||
if(s->pid == pid)
|
||||
break;
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
snprintf(txt, sizeof(txt),
|
||||
"exited, status=%d", WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
snprintf(txt, sizeof(txt),
|
||||
"killed by signal %d", WTERMSIG(status));
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
snprintf(txt, sizeof(txt),
|
||||
"stopped by signal %d", WSTOPSIG(status));
|
||||
} else if (WIFCONTINUED(status)) {
|
||||
snprintf(txt, sizeof(txt),
|
||||
"continued");
|
||||
} else {
|
||||
snprintf(txt, sizeof(txt),
|
||||
"unknown status");
|
||||
}
|
||||
|
||||
if(s != NULL) {
|
||||
LIST_REMOVE(s, link);
|
||||
free((void *)s->name);
|
||||
free(s);
|
||||
}
|
||||
pthread_mutex_unlock(&spawn_mutex);
|
||||
res = -EIO;
|
||||
if (WIFEXITED(status)) {
|
||||
res = WEXITSTATUS(status);
|
||||
if (stxt)
|
||||
snprintf(stxt, stxtlen, "exited, status=%d", WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
snprintf(stxt, stxtlen, "killed by signal %d, "
|
||||
"stopped by signal %d",
|
||||
WTERMSIG(status),
|
||||
WSTOPSIG(status));
|
||||
} else if (WIFCONTINUED(status)) {
|
||||
snprintf(stxt, stxtlen, "continued");
|
||||
} else {
|
||||
snprintf(stxt, stxtlen, "unknown status");
|
||||
}
|
||||
|
||||
if(s != NULL) {
|
||||
LIST_REMOVE(s, link);
|
||||
free((void *)s->name);
|
||||
free(s);
|
||||
}
|
||||
pthread_mutex_unlock(&spawn_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* The reaper is called once a second to finish of any pending spawns
|
||||
*/
|
||||
void
|
||||
spawn_reaper(void)
|
||||
{
|
||||
while (spawn_reap(NULL, 0) != -EAGAIN) ;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ int spawn_and_store_stdout(const char *prog, char *argv[], char **outp);
|
|||
|
||||
int spawnv(const char *prog, char *argv[]);
|
||||
|
||||
int spawn_reap(char *stxt, size_t stxtlen);
|
||||
|
||||
void spawn_reaper(void);
|
||||
|
||||
#endif /* SPAWN_H */
|
||||
|
|
|
@ -90,6 +90,7 @@ subscription_link_service(th_subscription_t *s, service_t *t)
|
|||
|
||||
sm = streaming_msg_create_code(SMT_GRACE, s->ths_postpone + t->s_grace_delay);
|
||||
streaming_pad_deliver(&t->s_streaming_pad, sm);
|
||||
streaming_msg_free(sm);
|
||||
|
||||
if(s->ths_start_message != NULL && t->s_streaming_status & TSS_PACKETS) {
|
||||
|
||||
|
|
|
@ -177,6 +177,7 @@ void gtimer_disarm(gtimer_t *gti);
|
|||
/*
|
||||
* List / Queue header declarations
|
||||
*/
|
||||
LIST_HEAD(access_entry_list, access_entry);
|
||||
LIST_HEAD(th_subscription_list, th_subscription);
|
||||
LIST_HEAD(dvr_config_list, dvr_config);
|
||||
LIST_HEAD(dvr_entry_list, dvr_entry);
|
||||
|
|
|
@ -139,11 +139,13 @@ static void
|
|||
comet_access_update(http_connection_t *hc, comet_mailbox_t *cmb)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
const char *username = hc->hc_access ? (hc->hc_access->aa_username ?: "") : "";
|
||||
|
||||
htsmsg_add_str(m, "notificationClass", "accessUpdate");
|
||||
|
||||
htsmsg_add_u32(m, "dvr", !http_access_verify(hc, ACCESS_RECORDER));
|
||||
htsmsg_add_u32(m, "admin", !http_access_verify(hc, ACCESS_ADMIN));
|
||||
htsmsg_add_str(m, "username", username);
|
||||
htsmsg_add_u32(m, "dvr", !http_access_verify(hc, ACCESS_RECORDER));
|
||||
htsmsg_add_u32(m, "admin", !http_access_verify(hc, ACCESS_ADMIN));
|
||||
|
||||
if(cmb->cmb_messages == NULL)
|
||||
cmb->cmb_messages = htsmsg_create_list();
|
||||
|
|
|
@ -153,10 +153,6 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque)
|
|||
extjs_load(hq, "static/app/esfilter.js");
|
||||
#if ENABLE_MPEGTS
|
||||
extjs_load(hq, "static/app/mpegts.js");
|
||||
#endif
|
||||
extjs_load(hq, "static/app/iptv.js");
|
||||
#if ENABLE_V4L
|
||||
extjs_load(hq, "static/app/v4l.js");
|
||||
#endif
|
||||
#if ENABLE_TIMESHIFT
|
||||
extjs_load(hq, "static/app/timeshift.js");
|
||||
|
@ -256,7 +252,7 @@ page_about(http_connection_t *hc, const char *remain, void *opaque)
|
|||
"<div class=\"about-title\">"
|
||||
"HTS Tvheadend %s"
|
||||
"</div><br>"
|
||||
"© 2006 - 2013 Andreas \303\226man, et al.<br><br>"
|
||||
"© 2006 - 2014 Andreas \303\226man, et al.<br><br>"
|
||||
"<img src=\"docresources/tvheadendlogo.png\"><br>"
|
||||
"<a href=\"https://tvheadend.org\">"
|
||||
"https://tvheadend.org</a><br><br>"
|
||||
|
@ -362,24 +358,6 @@ extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* EPG Content Groups
|
||||
*/
|
||||
static int
|
||||
extjs_ecglist(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
htsmsg_t *out, *array;
|
||||
|
||||
out = htsmsg_create_map();
|
||||
array = epg_genres_list_all(1, 0);
|
||||
htsmsg_add_msg(out, "entries", array);
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
http_output_content(hc, "text/x-json; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -502,127 +480,6 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
extjs_confignames(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");
|
||||
htsmsg_t *out, *array, *e;
|
||||
dvr_config_t *cfg;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
if(op != NULL && !strcmp(op, "list")) {
|
||||
|
||||
out = htsmsg_create_map();
|
||||
array = htsmsg_create_list();
|
||||
|
||||
if (http_access_verify(hc, ACCESS_RECORDER_ALL))
|
||||
goto skip;
|
||||
|
||||
LIST_FOREACH(cfg, &dvrconfigs, config_link) {
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_str(e, "identifier", cfg->dvr_config_name);
|
||||
if (strlen(cfg->dvr_config_name) == 0)
|
||||
htsmsg_add_str(e, "name", "(default)");
|
||||
else
|
||||
htsmsg_add_str(e, "name", cfg->dvr_config_name);
|
||||
htsmsg_add_msg(array, NULL, e);
|
||||
}
|
||||
|
||||
skip:
|
||||
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");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvr_containers(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");
|
||||
htsmsg_t *out, *array;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
if(op != NULL && !strcmp(op, "list")) {
|
||||
|
||||
out = htsmsg_create_map();
|
||||
array = htsmsg_create_list();
|
||||
|
||||
muxer_container_list(array);
|
||||
|
||||
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");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvr_caches(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");
|
||||
htsmsg_t *out, *array;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
if(op != NULL && !strcmp(op, "list")) {
|
||||
|
||||
out = htsmsg_create_map();
|
||||
array = htsmsg_create_list();
|
||||
|
||||
muxer_cache_list(array);
|
||||
|
||||
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");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -728,8 +585,8 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
|
|||
else
|
||||
limit = 20; /* XXX */
|
||||
|
||||
if ((s = http_arg_get(&hc->hc_req_args, "contenttype"))) {
|
||||
genre.code = atoi(s);
|
||||
if ((s = http_arg_get(&hc->hc_req_args, "content_type"))) {
|
||||
genre.code = atoi(s) * 16;
|
||||
eg = &genre;
|
||||
}
|
||||
|
||||
|
@ -788,7 +645,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
|
|||
htsmsg_add_str(m, "serieslink", e->serieslink->uri);
|
||||
|
||||
if((eg = LIST_FIRST(&ee->genre))) {
|
||||
htsmsg_add_u32(m, "contenttype", eg->code);
|
||||
htsmsg_add_u32(m, "content_type", eg->code / 16);
|
||||
}
|
||||
|
||||
dvr_entry_t *de;
|
||||
|
@ -930,501 +787,6 @@ extjs_epgobject(http_connection_t *hc, const char *remain, void *opaque)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvr(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");
|
||||
htsmsg_t *out, *r;
|
||||
dvr_entry_t *de;
|
||||
const char *s;
|
||||
int flags = 0;
|
||||
dvr_config_t *cfg;
|
||||
epg_broadcast_t *e;
|
||||
char buffer[5]; // Permissions buffer: leading zero, three octal digits plus terminating null
|
||||
|
||||
if(op == NULL)
|
||||
op = "loadSettings";
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
if(http_access_verify(hc, ACCESS_RECORDER)) {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_UNAUTHORIZED;
|
||||
}
|
||||
|
||||
if(!strcmp(op, "recordEvent") || !strcmp(op, "recordSeries")) {
|
||||
|
||||
const char *config_name = http_arg_get(&hc->hc_req_args, "config_name");
|
||||
|
||||
s = http_arg_get(&hc->hc_req_args, "eventId");
|
||||
if((e = epg_broadcast_find_by_id(atoi(s), NULL)) == NULL) {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (http_access_verify(hc, ACCESS_RECORDER_ALL)) {
|
||||
config_name = NULL;
|
||||
LIST_FOREACH(cfg, &dvrconfigs, config_link) {
|
||||
if (cfg->dvr_config_name && hc->hc_username &&
|
||||
strcmp(cfg->dvr_config_name, hc->hc_username) == 0) {
|
||||
config_name = cfg->dvr_config_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (config_name == NULL && hc->hc_username)
|
||||
tvhlog(LOG_INFO,"dvr","User '%s' has no dvr config with identical name, using default...", hc->hc_username);
|
||||
}
|
||||
|
||||
if (!strcmp(op, "recordEvent"))
|
||||
dvr_entry_create_by_event(config_name,
|
||||
e, 0, 0,
|
||||
hc->hc_representative, NULL, DVR_PRIO_NORMAL);
|
||||
else
|
||||
dvr_autorec_add_series_link(config_name, e, hc->hc_representative, "Created from EPG query");
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
} else if(!strcmp(op, "cancelEntry")) {
|
||||
s = http_arg_get(&hc->hc_req_args, "entryId");
|
||||
|
||||
if((de = dvr_entry_find_by_id(atoi(s))) == NULL) {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
dvr_entry_cancel(de);
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else if(!strcmp(op, "deleteEntry")) {
|
||||
s = http_arg_get(&hc->hc_req_args, "entryId");
|
||||
|
||||
if((de = dvr_entry_find_by_id(atoi(s))) == NULL) {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
dvr_entry_delete(de);
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else if(!strcmp(op, "createEntry")) {
|
||||
|
||||
const char *config_name = http_arg_get(&hc->hc_req_args, "config_name");
|
||||
const char *title = http_arg_get(&hc->hc_req_args, "title");
|
||||
const char *datestr = http_arg_get(&hc->hc_req_args, "date");
|
||||
const char *startstr = http_arg_get(&hc->hc_req_args, "starttime");
|
||||
const char *stopstr = http_arg_get(&hc->hc_req_args, "stoptime");
|
||||
const char *channel = http_arg_get(&hc->hc_req_args, "channelid");
|
||||
const char *pri = http_arg_get(&hc->hc_req_args, "pri");
|
||||
|
||||
channel_t *ch = channel ? channel_find(channel) : NULL;
|
||||
|
||||
if(ch == NULL || title == NULL ||
|
||||
datestr == NULL || strlen(datestr) != 10 ||
|
||||
startstr == NULL || strlen(startstr) != 5 ||
|
||||
stopstr == NULL || strlen(stopstr) != 5) {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
struct tm t = {0};
|
||||
t.tm_year = atoi(datestr + 6) - 1900;
|
||||
t.tm_mon = atoi(datestr) - 1;
|
||||
t.tm_mday = atoi(datestr + 3);
|
||||
t.tm_isdst = -1;
|
||||
|
||||
t.tm_hour = atoi(startstr);
|
||||
t.tm_min = atoi(startstr + 3);
|
||||
|
||||
time_t start = mktime(&t);
|
||||
|
||||
t.tm_hour = atoi(stopstr);
|
||||
t.tm_min = atoi(stopstr + 3);
|
||||
|
||||
time_t stop = mktime(&t);
|
||||
|
||||
if(stop < start)
|
||||
stop += 86400;
|
||||
|
||||
if (http_access_verify(hc, ACCESS_RECORDER_ALL)) {
|
||||
config_name = NULL;
|
||||
LIST_FOREACH(cfg, &dvrconfigs, config_link) {
|
||||
if (cfg->dvr_config_name && hc->hc_username &&
|
||||
strcmp(cfg->dvr_config_name, hc->hc_username) == 0) {
|
||||
config_name = cfg->dvr_config_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (config_name == NULL && hc->hc_username)
|
||||
tvhlog(LOG_INFO,"dvr","User '%s' has no dvr config with identical name, using default...", hc->hc_username);
|
||||
}
|
||||
|
||||
dvr_entry_create(config_name,
|
||||
ch, start, stop, 0, 0, title, NULL, NULL,
|
||||
0, hc->hc_representative,
|
||||
NULL, dvr_pri2val(pri));
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else if(!strcmp(op, "createAutoRec")) {
|
||||
int min_duration;
|
||||
int max_duration;
|
||||
epg_genre_t genre, *eg = NULL;
|
||||
|
||||
if ((s = http_arg_get(&hc->hc_req_args, "contenttype"))) {
|
||||
genre.code = atoi(s);
|
||||
eg = &genre;
|
||||
}
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "minduration")) != NULL)
|
||||
min_duration = atoi(s);
|
||||
else
|
||||
min_duration = 0;
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "maxduration")) != NULL)
|
||||
max_duration = atoi(s);
|
||||
else
|
||||
max_duration = INT_MAX;
|
||||
|
||||
dvr_autorec_add(http_arg_get(&hc->hc_req_args, "config_name"),
|
||||
http_arg_get(&hc->hc_req_args, "title"),
|
||||
http_arg_get(&hc->hc_req_args, "channel"),
|
||||
http_arg_get(&hc->hc_req_args, "tag"),
|
||||
eg, min_duration,max_duration,
|
||||
hc->hc_representative, "Created from EPG query");
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else if(!strcmp(op, "loadSettings")) {
|
||||
|
||||
s = http_arg_get(&hc->hc_req_args, "config_name");
|
||||
if (s == NULL)
|
||||
s = "";
|
||||
cfg = dvr_config_find_by_name_default(s);
|
||||
|
||||
r = htsmsg_create_map();
|
||||
htsmsg_add_str(r, "storage", cfg->dvr_storage);
|
||||
htsmsg_add_str(r, "charset", cfg->dvr_charset ? cfg->dvr_charset : "UTF-8");
|
||||
htsmsg_add_str(r, "container", muxer_container_type2txt(cfg->dvr_mc));
|
||||
|
||||
/* Convert integer permissions to an octal-format 0xxx string and store it in the config file */
|
||||
|
||||
snprintf(buffer,sizeof(buffer),"%04o",cfg->dvr_muxcnf.m_file_permissions);
|
||||
htsmsg_add_str(r, "filePermissions", buffer);
|
||||
snprintf(buffer,sizeof(buffer),"%04o",cfg->dvr_muxcnf.m_directory_permissions);
|
||||
htsmsg_add_str(r, "dirPermissions", buffer);
|
||||
|
||||
htsmsg_add_u32(r, "cache", cfg->dvr_muxcnf.m_cache);
|
||||
htsmsg_add_u32(r, "rewritePAT",
|
||||
!!(cfg->dvr_muxcnf.m_flags & MC_REWRITE_PAT));
|
||||
htsmsg_add_u32(r, "rewritePMT",
|
||||
!!(cfg->dvr_muxcnf.m_flags & MC_REWRITE_PMT));
|
||||
if(cfg->dvr_postproc != NULL)
|
||||
htsmsg_add_str(r, "postproc", cfg->dvr_postproc);
|
||||
htsmsg_add_u32(r, "retention", cfg->dvr_retention_days);
|
||||
htsmsg_add_u32(r, "preExtraTime", cfg->dvr_extra_time_pre);
|
||||
htsmsg_add_u32(r, "postExtraTime", cfg->dvr_extra_time_post);
|
||||
htsmsg_add_u32(r, "dayDirs", !!(cfg->dvr_flags & DVR_DIR_PER_DAY));
|
||||
htsmsg_add_u32(r, "channelDirs", !!(cfg->dvr_flags & DVR_DIR_PER_CHANNEL));
|
||||
htsmsg_add_u32(r, "channelInTitle", !!(cfg->dvr_flags & DVR_CHANNEL_IN_TITLE));
|
||||
htsmsg_add_u32(r, "dateInTitle", !!(cfg->dvr_flags & DVR_DATE_IN_TITLE));
|
||||
htsmsg_add_u32(r, "timeInTitle", !!(cfg->dvr_flags & DVR_TIME_IN_TITLE));
|
||||
htsmsg_add_u32(r, "whitespaceInTitle", !!(cfg->dvr_flags & DVR_WHITESPACE_IN_TITLE));
|
||||
htsmsg_add_u32(r, "titleDirs", !!(cfg->dvr_flags & DVR_DIR_PER_TITLE));
|
||||
htsmsg_add_u32(r, "episodeInTitle", !!(cfg->dvr_flags & DVR_EPISODE_IN_TITLE));
|
||||
htsmsg_add_u32(r, "cleanTitle", !!(cfg->dvr_flags & DVR_CLEAN_TITLE));
|
||||
htsmsg_add_u32(r, "tagFiles", !!(cfg->dvr_flags & DVR_TAG_FILES));
|
||||
htsmsg_add_u32(r, "commSkip", !!(cfg->dvr_flags & DVR_SKIP_COMMERCIALS));
|
||||
htsmsg_add_u32(r, "subtitleInTitle", !!(cfg->dvr_flags & DVR_SUBTITLE_IN_TITLE));
|
||||
htsmsg_add_u32(r, "episodeBeforeDate", !!(cfg->dvr_flags & DVR_EPISODE_BEFORE_DATE));
|
||||
htsmsg_add_u32(r, "episodeDuplicateDetection", !!(cfg->dvr_flags & DVR_EPISODE_DUPLICATE_DETECTION));
|
||||
|
||||
out = json_single_record(r, "dvrSettings");
|
||||
|
||||
} else if(!strcmp(op, "saveSettings")) {
|
||||
|
||||
s = http_arg_get(&hc->hc_req_args, "config_name");
|
||||
cfg = dvr_config_find_by_name(s);
|
||||
if (cfg == NULL)
|
||||
cfg = dvr_config_create(s);
|
||||
|
||||
tvhlog(LOG_INFO,"dvr","Saving configuration '%s'", cfg->dvr_config_name);
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "storage")) != NULL)
|
||||
dvr_storage_set(cfg,s);
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "charset")) != NULL)
|
||||
dvr_charset_set(cfg,s);
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "container")) != NULL)
|
||||
dvr_container_set(cfg,s);
|
||||
|
||||
/*
|
||||
* Convert 0xxx format permission strings to integer for internal use
|
||||
* Note no checking that strtol won't overflow int - this should never happen with three-digit numbers
|
||||
*/
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "filePermissions")) != NULL)
|
||||
dvr_file_permissions_set(cfg,(int)strtol(s,NULL,0));
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "dirPermissions")) != NULL)
|
||||
dvr_directory_permissions_set(cfg,(int)strtol(s,NULL,0));
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "cache")) != NULL)
|
||||
dvr_mux_cache_set(cfg,atoi(s));
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "postproc")) != NULL)
|
||||
dvr_postproc_set(cfg,s);
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "retention")) != NULL)
|
||||
dvr_retention_set(cfg,atoi(s));
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "preExtraTime")) != NULL)
|
||||
dvr_extra_time_pre_set(cfg,atoi(s));
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "postExtraTime")) != NULL)
|
||||
dvr_extra_time_post_set(cfg,atoi(s));
|
||||
|
||||
if(http_arg_get(&hc->hc_req_args, "dayDirs") != NULL)
|
||||
flags |= DVR_DIR_PER_DAY;
|
||||
if(http_arg_get(&hc->hc_req_args, "channelDirs") != NULL)
|
||||
flags |= DVR_DIR_PER_CHANNEL;
|
||||
if(http_arg_get(&hc->hc_req_args, "channelInTitle") != NULL)
|
||||
flags |= DVR_CHANNEL_IN_TITLE;
|
||||
if(http_arg_get(&hc->hc_req_args, "cleanTitle") != NULL)
|
||||
flags |= DVR_CLEAN_TITLE;
|
||||
if(http_arg_get(&hc->hc_req_args, "dateInTitle") != NULL)
|
||||
flags |= DVR_DATE_IN_TITLE;
|
||||
if(http_arg_get(&hc->hc_req_args, "timeInTitle") != NULL)
|
||||
flags |= DVR_TIME_IN_TITLE;
|
||||
if(http_arg_get(&hc->hc_req_args, "whitespaceInTitle") != NULL)
|
||||
flags |= DVR_WHITESPACE_IN_TITLE;
|
||||
if(http_arg_get(&hc->hc_req_args, "titleDirs") != NULL)
|
||||
flags |= DVR_DIR_PER_TITLE;
|
||||
if(http_arg_get(&hc->hc_req_args, "episodeInTitle") != NULL)
|
||||
flags |= DVR_EPISODE_IN_TITLE;
|
||||
if(http_arg_get(&hc->hc_req_args, "tagFiles") != NULL)
|
||||
flags |= DVR_TAG_FILES;
|
||||
if(http_arg_get(&hc->hc_req_args, "commSkip") != NULL)
|
||||
flags |= DVR_SKIP_COMMERCIALS;
|
||||
if(http_arg_get(&hc->hc_req_args, "subtitleInTitle") != NULL)
|
||||
flags |= DVR_SUBTITLE_IN_TITLE;
|
||||
if(http_arg_get(&hc->hc_req_args, "episodeBeforeDate") != NULL)
|
||||
flags |= DVR_EPISODE_BEFORE_DATE;
|
||||
if(http_arg_get(&hc->hc_req_args, "episodeDuplicateDetection") != NULL)
|
||||
flags |= DVR_EPISODE_DUPLICATE_DETECTION;
|
||||
|
||||
|
||||
dvr_flags_set(cfg,flags);
|
||||
|
||||
/* Muxer flags */
|
||||
flags = 0;
|
||||
if(http_arg_get(&hc->hc_req_args, "rewritePAT") != NULL)
|
||||
flags |= MC_REWRITE_PAT;
|
||||
if(http_arg_get(&hc->hc_req_args, "rewritePMT") != NULL)
|
||||
flags |= MC_REWRITE_PMT;
|
||||
|
||||
dvr_mux_flags_set(cfg, flags);
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else if(!strcmp(op, "deleteSettings")) {
|
||||
|
||||
s = http_arg_get(&hc->hc_req_args, "config_name");
|
||||
dvr_config_delete(s);
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} 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");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque,
|
||||
dvr_entry_filter filter, dvr_entry_comparator cmp)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
htsmsg_t *out, *array, *m;
|
||||
dvr_query_result_t dqr;
|
||||
dvr_entry_t *de;
|
||||
int start = 0, end, limit, i;
|
||||
const char *s;
|
||||
int64_t fsize = 0;
|
||||
char buf[100];
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "start")) != NULL)
|
||||
start = atoi(s);
|
||||
|
||||
if((s = http_arg_get(&hc->hc_req_args, "limit")) != NULL)
|
||||
limit = atoi(s);
|
||||
else
|
||||
limit = 20; /* XXX */
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
if(http_access_verify(hc, ACCESS_RECORDER)) {
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return HTTP_STATUS_UNAUTHORIZED;
|
||||
}
|
||||
|
||||
out = htsmsg_create_map();
|
||||
array = htsmsg_create_list();
|
||||
|
||||
|
||||
dvr_query_filter(&dqr, filter);
|
||||
|
||||
dvr_query_sort_cmp(&dqr, cmp);
|
||||
|
||||
htsmsg_add_u32(out, "totalCount", dqr.dqr_entries);
|
||||
|
||||
start = MIN(start, dqr.dqr_entries);
|
||||
end = MIN(start + limit, dqr.dqr_entries);
|
||||
|
||||
for(i = start; i < end; i++) {
|
||||
de = dqr.dqr_array[i];
|
||||
|
||||
m = htsmsg_create_map();
|
||||
|
||||
htsmsg_add_str(m, "channel", DVR_CH_NAME(de));
|
||||
if(de->de_channel != NULL) {
|
||||
htsmsg_add_str(m, "channelid", channel_get_uuid(de->de_channel));
|
||||
if (de->de_channel->ch_icon)
|
||||
htsmsg_add_imageurl(m, "chicon", "imagecache/%d",
|
||||
de->de_channel->ch_icon);
|
||||
}
|
||||
|
||||
htsmsg_add_str(m, "config_name", de->de_config_name);
|
||||
|
||||
if(de->de_title != NULL)
|
||||
htsmsg_add_str(m, "title", lang_str_get(de->de_title, NULL));
|
||||
|
||||
if(de->de_desc != NULL)
|
||||
htsmsg_add_str(m, "description", lang_str_get(de->de_desc, NULL));
|
||||
|
||||
if (de->de_bcast && de->de_bcast->episode)
|
||||
if (epg_episode_number_format(de->de_bcast->episode, buf, 100, NULL, "Season %d", ".", "Episode %d", "/%d"))
|
||||
htsmsg_add_str(m, "episode", buf);
|
||||
|
||||
htsmsg_add_u32(m, "id", de->de_id);
|
||||
htsmsg_add_u32(m, "start", de->de_start);
|
||||
htsmsg_add_u32(m, "end", de->de_stop);
|
||||
htsmsg_add_u32(m, "duration", de->de_stop - de->de_start);
|
||||
|
||||
htsmsg_add_str(m, "creator", de->de_creator);
|
||||
|
||||
htsmsg_add_str(m, "pri", dvr_val2pri(de->de_pri));
|
||||
|
||||
htsmsg_add_str(m, "status", dvr_entry_status(de));
|
||||
htsmsg_add_str(m, "schedstate", dvr_entry_schedstatus(de));
|
||||
|
||||
|
||||
if(de->de_sched_state == DVR_COMPLETED) {
|
||||
fsize = dvr_get_filesize(de);
|
||||
if (fsize > 0) {
|
||||
char url[100];
|
||||
htsmsg_add_s64(m, "filesize", fsize);
|
||||
snprintf(url, sizeof(url), "dvrfile/%d", de->de_id);
|
||||
htsmsg_add_str(m, "url", url);
|
||||
}
|
||||
}
|
||||
|
||||
htsmsg_add_msg(array, NULL, m);
|
||||
}
|
||||
|
||||
dvr_query_free(&dqr);
|
||||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
htsmsg_add_msg(out, "entries", array);
|
||||
|
||||
htsmsg_json_serialize(out, hq, 0);
|
||||
htsmsg_destroy(out);
|
||||
http_output_content(hc, "text/x-json; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_dvr_entry_finished(dvr_entry_t *entry)
|
||||
{
|
||||
dvr_entry_sched_state_t state = entry->de_sched_state;
|
||||
return state == DVR_COMPLETED && !entry->de_last_error && dvr_get_filesize(entry) != -1;
|
||||
}
|
||||
|
||||
static int is_dvr_entry_upcoming(dvr_entry_t *entry)
|
||||
{
|
||||
dvr_entry_sched_state_t state = entry->de_sched_state;
|
||||
return state == DVR_RECORDING || state == DVR_SCHEDULED;
|
||||
}
|
||||
|
||||
|
||||
static int is_dvr_entry_failed(dvr_entry_t *entry)
|
||||
{
|
||||
if (is_dvr_entry_finished(entry))
|
||||
return 0;
|
||||
if (is_dvr_entry_upcoming(entry))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
extjs_dvrlist_finished(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_finished, dvr_sort_start_descending);
|
||||
}
|
||||
|
||||
static int
|
||||
extjs_dvrlist_upcoming(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_upcoming, dvr_sort_start_ascending);
|
||||
}
|
||||
|
||||
static int
|
||||
extjs_dvrlist_failed(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
return extjs_dvrlist(hc, remain, opaque, is_dvr_entry_failed, dvr_sort_start_descending);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
extjs_service_delete(htsmsg_t *in)
|
||||
{
|
||||
htsmsg_field_t *f;
|
||||
service_t *t;
|
||||
const char *id;
|
||||
|
||||
TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
|
||||
if((id = htsmsg_field_get_string(f)) != NULL &&
|
||||
(t = service_find_by_identifier(id)) != NULL)
|
||||
service_destroy(t, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -1700,17 +1062,9 @@ extjs_start(void)
|
|||
http_path_add("/capabilities", NULL, extjs_capabilities, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/tablemgr", NULL, extjs_tablemgr, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/epggrab", NULL, extjs_epggrab, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/confignames", NULL, extjs_confignames, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/epg", NULL, extjs_epg, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/epgrelated", NULL, extjs_epgrelated, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/epgobject", NULL, extjs_epgobject, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/dvr", NULL, extjs_dvr, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/dvrlist_upcoming", NULL, extjs_dvrlist_upcoming, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/dvrlist_finished", NULL, extjs_dvrlist_finished, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/dvrlist_failed", NULL, extjs_dvrlist_failed, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/dvr_containers", NULL, extjs_dvr_containers, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/dvr_caches", NULL, extjs_dvr_caches, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/ecglist", NULL, extjs_ecglist, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/config", NULL, extjs_config, ACCESS_WEB_INTERFACE);
|
||||
http_path_add("/languages", NULL, extjs_languages, ACCESS_WEB_INTERFACE);
|
||||
#if ENABLE_TIMESHIFT
|
||||
|
|
|
@ -171,7 +171,7 @@ page_simple(http_connection_t *hc,
|
|||
rstatus = val2str(de->de_sched_state, recstatustxt);
|
||||
|
||||
|
||||
htsbuf_qprintf(hq, "<a href=\"/pvrinfo/%d\">", de->de_id);
|
||||
htsbuf_qprintf(hq, "<a href=\"/pvrinfo/%s\">", idnode_uuid_as_str(&de->de_id));
|
||||
|
||||
htsbuf_qprintf(hq,
|
||||
"%02d:%02d-%02d:%02d %s",
|
||||
|
@ -216,7 +216,7 @@ page_einfo(http_connection_t *hc, const char *remain, void *opaque)
|
|||
de = dvr_entry_find_by_event(e);
|
||||
|
||||
if((http_arg_get(&hc->hc_req_args, "rec")) != NULL) {
|
||||
de = dvr_entry_create_by_event("", e, 0, 0, hc->hc_username ?: "anonymous", NULL,
|
||||
de = dvr_entry_create_by_event(NULL, e, 0, 0, hc->hc_username ?: "anonymous", NULL,
|
||||
DVR_PRIO_NORMAL);
|
||||
} else if(de != NULL && (http_arg_get(&hc->hc_req_args, "cancel")) != NULL) {
|
||||
de = dvr_entry_cancel(de);
|
||||
|
@ -328,8 +328,8 @@ page_pvrinfo(http_connection_t *hc, const char *remain, void *opaque)
|
|||
if((rstatus = val2str(de->de_sched_state, recstatustxt)) != NULL)
|
||||
htsbuf_qprintf(hq, "Recording status: %s<br>", rstatus);
|
||||
|
||||
htsbuf_qprintf(hq, "<form method=\"post\" action=\"/pvrinfo/%d\">",
|
||||
de->de_id);
|
||||
htsbuf_qprintf(hq, "<form method=\"post\" action=\"/pvrinfo/%s\">",
|
||||
idnode_uuid_as_str(&de->de_id));
|
||||
|
||||
switch(de->de_sched_state) {
|
||||
case DVR_SCHEDULED:
|
||||
|
|
|
@ -54,18 +54,27 @@ dumpchannels(htsbuf_queue_t *hq)
|
|||
{
|
||||
channel_t *ch;
|
||||
outputtitle(hq, 0, "Channels");
|
||||
int64_t chnum;
|
||||
char chbuf[32];
|
||||
|
||||
CHANNEL_FOREACH(ch) {
|
||||
|
||||
htsbuf_qprintf(hq, "%s (%d)\n", channel_get_name(ch), channel_get_id(ch));
|
||||
chnum = channel_get_number(ch);
|
||||
if (channel_get_minor(chnum))
|
||||
snprintf(chbuf, sizeof(chbuf), "%u.%u",
|
||||
channel_get_major(chnum),
|
||||
channel_get_minor(chnum));
|
||||
else
|
||||
snprintf(chbuf, sizeof(chbuf), "%u", channel_get_major(chnum));
|
||||
htsbuf_qprintf(hq,
|
||||
" refcount = %d\n"
|
||||
" zombie = %d\n"
|
||||
" number = %d\n"
|
||||
" number = %s\n"
|
||||
" icon = %s\n\n",
|
||||
ch->ch_refcount,
|
||||
ch->ch_zombie,
|
||||
channel_get_number(ch),
|
||||
chbuf,
|
||||
ch->ch_icon ?: "<none set>");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,32 +2,47 @@
|
|||
* Access Control
|
||||
*/
|
||||
|
||||
tvheadend.acleditor = function(panel)
|
||||
tvheadend.acleditor = function(panel, index)
|
||||
{
|
||||
panel = new Ext.TabPanel({
|
||||
activeTab: 0,
|
||||
autoScroll: true,
|
||||
title: 'Access Control',
|
||||
iconCls: 'group',
|
||||
items: []
|
||||
});
|
||||
var list = 'enabled,username,password,prefix,streaming,adv_streaming,' +
|
||||
'dvr,dvr_config,webui,admin,channel_min,channel_max,channel_tag,' +
|
||||
'comment';
|
||||
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/access/entry',
|
||||
comet: 'acl_entries',
|
||||
titleS: 'Access Entry',
|
||||
titleP: 'Access Entries',
|
||||
tabIndex: 0,
|
||||
iconCls: 'group',
|
||||
columns: {
|
||||
username: { width: 250 },
|
||||
password: { width: 250 },
|
||||
prefix: { width: 350 },
|
||||
streaming: { width: 100 },
|
||||
adv_streaming: { width: 100 },
|
||||
dvr: { width: 100 },
|
||||
webui: { width: 100 },
|
||||
admin: { width: 100 },
|
||||
channel_min: { width: 100 },
|
||||
channel_max: { width: 100 },
|
||||
},
|
||||
tabIndex: index,
|
||||
edit: {
|
||||
params: {
|
||||
list: list,
|
||||
},
|
||||
},
|
||||
add: {
|
||||
url: 'api/access/entry',
|
||||
params: {
|
||||
list: list,
|
||||
},
|
||||
create: { }
|
||||
},
|
||||
del: true,
|
||||
move: true,
|
||||
list: list,
|
||||
help: function() {
|
||||
new tvheadend.help('Access Control Entries', 'config_access.html');
|
||||
},
|
||||
});
|
||||
|
||||
return panel;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
tvheadend.capmteditor = function() {
|
||||
tvheadend.capmteditor = function(panel, index) {
|
||||
|
||||
var fm = Ext.form;
|
||||
|
||||
function setMetaAttr(meta, record) {
|
||||
|
@ -104,6 +105,8 @@ tvheadend.capmteditor = function() {
|
|||
}
|
||||
});
|
||||
|
||||
return new tvheadend.tableEditor('Capmt Connections', 'capmt', cm, rec,
|
||||
var p = new tvheadend.tableEditor('Capmt Connections', 'capmt', cm, rec,
|
||||
[], store, 'config_capmt.html', 'key');
|
||||
|
||||
tvheadend.paneladd(panel, p, index);
|
||||
};
|
||||
|
|
|
@ -6,16 +6,9 @@ insertChannelTagsClearOption = function( scope, records, options ){
|
|||
scope.insert(0,new placeholder({key: '-1', val: '(Clear filter)'}));
|
||||
};
|
||||
|
||||
tvheadend.channelTags = new Ext.data.JsonStore({
|
||||
tvheadend.channelTags = tvheadend.idnode_get_enum({
|
||||
url: 'api/channeltag/list',
|
||||
root: 'entries',
|
||||
fields: ['key', 'val'],
|
||||
id: 'key',
|
||||
autoLoad: true,
|
||||
sortInfo: {
|
||||
field: 'val',
|
||||
direction: 'ASC',
|
||||
},
|
||||
event: 'channeltag',
|
||||
listeners: {
|
||||
'load': insertChannelTagsClearOption
|
||||
}
|
||||
|
@ -60,11 +53,7 @@ tvheadend.comet.on('channels', function(m) {
|
|||
|
||||
tvheadend.channel_tab = function(panel, index)
|
||||
{
|
||||
function assign_low_number() {
|
||||
var tab = panel.getActiveTab();
|
||||
var sm = tab.getSelectionModel();
|
||||
var store = tab.getStore();
|
||||
|
||||
function assign_low_number(ctx, e, store, sm) {
|
||||
if (sm.getCount() !== 1)
|
||||
return;
|
||||
|
||||
|
@ -107,20 +96,14 @@ tvheadend.channel_tab = function(panel, index)
|
|||
sm.selectNext();
|
||||
}
|
||||
|
||||
function move_number_up() {
|
||||
var tab = panel.getActiveTab();
|
||||
var sm = tab.getSelectionModel();
|
||||
|
||||
function move_number_up(ctx, e, store, sm) {
|
||||
Ext.each(sm.getSelections(), function(channel) {
|
||||
var number = channel.data.number;
|
||||
channel.set('number', number + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function move_number_down() {
|
||||
var tab = panel.getActiveTab();
|
||||
var sm = tab.getSelectionModel();
|
||||
|
||||
function move_number_down(ctx, e, store, sm) {
|
||||
Ext.each(sm.getSelections(), function(channel) {
|
||||
var number = channel.data.number;
|
||||
|
||||
|
@ -130,11 +113,7 @@ tvheadend.channel_tab = function(panel, index)
|
|||
});
|
||||
}
|
||||
|
||||
function swap_numbers() {
|
||||
var tab = panel.getActiveTab();
|
||||
var sm = tab.getSelectionModel();
|
||||
var store = tab.getStore(); //store is unused
|
||||
|
||||
function swap_numbers(ctx, e, store, sm) {
|
||||
if (sm.getCount() !== 2)
|
||||
return;
|
||||
|
||||
|
@ -145,45 +124,70 @@ tvheadend.channel_tab = function(panel, index)
|
|||
sel[1].set('number', tmp);
|
||||
}
|
||||
|
||||
var mapButton = new Ext.Toolbar.Button({
|
||||
tooltip: 'Map services to channels',
|
||||
iconCls: 'clone',
|
||||
text: 'Map Services',
|
||||
handler: tvheadend.service_mapper,
|
||||
disabled: false
|
||||
});
|
||||
var mapButton = {
|
||||
name: 'map',
|
||||
builder: function() {
|
||||
return new Ext.Toolbar.Button({
|
||||
tooltip: 'Map services to channels',
|
||||
iconCls: 'clone',
|
||||
text: 'Map Services',
|
||||
disabled: false
|
||||
});
|
||||
},
|
||||
callback: tvheadend.service_mapper
|
||||
};
|
||||
|
||||
var lowNoButton = new Ext.Toolbar.Button({
|
||||
tooltip: 'Assign lowest free channel number',
|
||||
iconCls: 'bullet_add',
|
||||
text: 'Assign Number',
|
||||
handler: assign_low_number,
|
||||
disabled: false
|
||||
});
|
||||
var lowNoButton = {
|
||||
name: 'lowno',
|
||||
builder: function() {
|
||||
return new Ext.Toolbar.Button({
|
||||
tooltip: 'Assign lowest free channel number',
|
||||
iconCls: 'bullet_add',
|
||||
text: 'Assign Number',
|
||||
disabled: false
|
||||
});
|
||||
},
|
||||
callback: assign_low_number
|
||||
};
|
||||
|
||||
var noUpButton = new Ext.Toolbar.Button({
|
||||
tooltip: 'Move channel one number up',
|
||||
iconCls: 'arrow_up',
|
||||
text: 'Number Up',
|
||||
handler: move_number_up,
|
||||
disabled: false
|
||||
});
|
||||
var noUpButton = {
|
||||
name: 'noup',
|
||||
builder: function() {
|
||||
return new Ext.Toolbar.Button({
|
||||
tooltip: 'Move channel one number up',
|
||||
iconCls: 'arrow_up',
|
||||
text: 'Number Up',
|
||||
disabled: false
|
||||
});
|
||||
},
|
||||
callback: move_number_up
|
||||
};
|
||||
|
||||
var noDownButton = new Ext.Toolbar.Button({
|
||||
tooltip: 'Move channel one number down',
|
||||
iconCls: 'arrow_down',
|
||||
text: 'Number Down',
|
||||
handler: move_number_down,
|
||||
disabled: false
|
||||
});
|
||||
var noDownButton = {
|
||||
name: 'nodown',
|
||||
builder: function() {
|
||||
return new Ext.Toolbar.Button({
|
||||
tooltip: 'Move channel one number down',
|
||||
iconCls: 'arrow_down',
|
||||
text: 'Number Down',
|
||||
disabled: false
|
||||
});
|
||||
},
|
||||
callback: move_number_down
|
||||
};
|
||||
|
||||
var noSwapButton = new Ext.Toolbar.Button({
|
||||
tooltip: 'Swap the two selected channels numbers',
|
||||
iconCls: 'arrow_switch',
|
||||
text: 'Swap Numbers',
|
||||
handler: swap_numbers,
|
||||
disabled: false
|
||||
});
|
||||
var noSwapButton = {
|
||||
name: 'swap',
|
||||
builder: function() {
|
||||
return new Ext.Toolbar.Button({
|
||||
tooltip: 'Swap the two selected channels numbers',
|
||||
iconCls: 'arrow_switch',
|
||||
text: 'Swap Numbers',
|
||||
disabled: false
|
||||
});
|
||||
},
|
||||
callback: swap_numbers
|
||||
};
|
||||
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/channel',
|
||||
|
|
|
@ -2,19 +2,7 @@
|
|||
* Comet interfaces
|
||||
*/
|
||||
Ext.extend(tvheadend.Comet = function() {
|
||||
this.addEvents({
|
||||
accessUpdate: true,
|
||||
tvAdapter: true,
|
||||
dvbMux: true,
|
||||
dvbStore: true,
|
||||
dvbSatConf: true,
|
||||
logmessage: true,
|
||||
channeltags: true,
|
||||
autorec: true,
|
||||
dvrdb: true,
|
||||
dvrconfig: true,
|
||||
channels: true
|
||||
});
|
||||
this.addEvents({ });
|
||||
}, Ext.util.Observable);
|
||||
|
||||
tvheadend.comet = new tvheadend.Comet();
|
||||
|
|
|
@ -31,7 +31,7 @@ tvheadend.comet.on('config', function(m) {
|
|||
}
|
||||
});
|
||||
|
||||
tvheadend.miscconf = function() {
|
||||
tvheadend.miscconf = function(panel, index) {
|
||||
|
||||
/*
|
||||
* Basic Config
|
||||
|
@ -240,7 +240,7 @@ tvheadend.miscconf = function() {
|
|||
if (imagecache_form)
|
||||
_items.push(imagecache_form);
|
||||
|
||||
var panel = new Ext.Panel({
|
||||
var mpanel = new Ext.Panel({
|
||||
title: 'General',
|
||||
iconCls: 'wrench',
|
||||
border: false,
|
||||
|
@ -251,6 +251,8 @@ tvheadend.miscconf = function() {
|
|||
tbar: [saveButton, '->', helpButton]
|
||||
});
|
||||
|
||||
tvheadend.paneladd(panel, mpanel, index);
|
||||
|
||||
/* ****************************************************************
|
||||
* Load/Save
|
||||
* ***************************************************************/
|
||||
|
@ -297,6 +299,4 @@ tvheadend.miscconf = function() {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
return panel;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
tvheadend.cwceditor = function() {
|
||||
tvheadend.cwceditor = function(panel, index) {
|
||||
var fm = Ext.form;
|
||||
|
||||
function setMetaAttr(meta, record) {
|
||||
|
@ -125,5 +125,5 @@ tvheadend.cwceditor = function() {
|
|||
}
|
||||
});
|
||||
|
||||
return grid;
|
||||
tvheadend.paneladd(panel, grid, index);
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,31 +13,22 @@ insertContentGroupClearOption = function( scope, records, options ){
|
|||
scope.insert(0,new placeholder({name: '(Clear filter)', code: '-1'}));
|
||||
};
|
||||
|
||||
//WIBNI: might want this store to periodically update
|
||||
|
||||
tvheadend.ContentGroupStore = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
fields: ['name', 'code'],
|
||||
autoLoad: true,
|
||||
url: 'ecglist',
|
||||
tvheadend.ContentGroupStore = tvheadend.idnode_get_enum({
|
||||
url: 'api/epg/content_type/list',
|
||||
listeners: {
|
||||
'load': insertContentGroupClearOption
|
||||
load: insertContentGroupClearOption
|
||||
}
|
||||
});
|
||||
|
||||
tvheadend.contentGroupLookupName = function(code) {
|
||||
ret = "";
|
||||
tvheadend.ContentGroupStore.each(function(r) {
|
||||
if (r.data.code === code)
|
||||
ret = r.data.name;
|
||||
else if (ret === "" && r.data.code === (code & 0xF0))
|
||||
ret = r.data.name;
|
||||
if (r.data.key === code)
|
||||
ret = r.data.val;
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
tvheadend.ContentGroupStore.setDefaultSort('code', 'ASC');
|
||||
|
||||
tvheadend.channelLookupName = function(key) {
|
||||
channelString = "";
|
||||
|
||||
|
@ -103,7 +94,7 @@ tvheadend.epgDetails = function(event) {
|
|||
content += '<div class="x-epg-desc">' + event.description + '</div>';
|
||||
content += '<div class="x-epg-meta">' + event.starrating + '</div>';
|
||||
content += '<div class="x-epg-meta">' + event.agerating + '</div>';
|
||||
content += '<div class="x-epg-meta">' + tvheadend.contentGroupLookupName(event.contenttype) + '</div>';
|
||||
content += '<div class="x-epg-meta">' + tvheadend.contentGroupLookupName(event.content_type) + '</div>';
|
||||
|
||||
if (event.ext_desc != null)
|
||||
content += '<div class="x-epg-meta">' + event.ext_desc + '</div>';
|
||||
|
@ -127,17 +118,56 @@ tvheadend.epgDetails = function(event) {
|
|||
'?title=' + encodeURIComponent(title) + '">Play</a></div>';
|
||||
}
|
||||
|
||||
var confcombo = new Ext.form.ComboBox({
|
||||
store: tvheadend.configNames,
|
||||
triggerAction: 'all',
|
||||
mode: 'local',
|
||||
valueField: 'identifier',
|
||||
displayField: 'name',
|
||||
name: 'config_name',
|
||||
emptyText: '(default)',
|
||||
value: '',
|
||||
editable: false
|
||||
});
|
||||
var buttons = [];
|
||||
|
||||
if (tvheadend.accessUpdate.dvr) {
|
||||
|
||||
var store = new Ext.data.JsonStore({
|
||||
autoload: true,
|
||||
root: 'entries',
|
||||
fields: ['key','val'],
|
||||
id: 'key',
|
||||
url: 'api/idnode/load',
|
||||
baseParams: {
|
||||
enum: 1,
|
||||
'class': 'dvrconfig'
|
||||
},
|
||||
sortInfo: {
|
||||
field: 'val',
|
||||
direction: 'ASC'
|
||||
}
|
||||
});
|
||||
store.load();
|
||||
|
||||
var confcombo = new Ext.form.ComboBox({
|
||||
store: store,
|
||||
triggerAction: 'all',
|
||||
mode: 'local',
|
||||
valueField: 'key',
|
||||
displayField: 'val',
|
||||
name: 'config_name',
|
||||
emptyText: '(default)',
|
||||
value: '',
|
||||
editable: false
|
||||
});
|
||||
|
||||
buttons.push(confcombo);
|
||||
buttons.push(new Ext.Button({
|
||||
handler: recordEvent,
|
||||
text: "Record program"
|
||||
}));
|
||||
buttons.push(new Ext.Button({
|
||||
handler: recordSeries,
|
||||
text: event.serieslink ? "Record series" : "Autorec"
|
||||
}));
|
||||
|
||||
} else {
|
||||
|
||||
buttons.push(new Ext.Button({
|
||||
handler: function() { win.close(); },
|
||||
text: "Close"
|
||||
}));
|
||||
}
|
||||
|
||||
var win = new Ext.Window({
|
||||
title: event.title,
|
||||
|
@ -145,33 +175,26 @@ tvheadend.epgDetails = function(event) {
|
|||
width: 500,
|
||||
height: 300,
|
||||
constrainHeader: true,
|
||||
buttons: [confcombo, new Ext.Button({
|
||||
handler: recordEvent,
|
||||
text: "Record program"
|
||||
}), new Ext.Button({
|
||||
handler: recordSeries,
|
||||
text: event.serieslink ? "Record series" : "Autorec"
|
||||
})],
|
||||
buttons: buttons,
|
||||
buttonAlign: 'center',
|
||||
html: content
|
||||
});
|
||||
win.show();
|
||||
|
||||
function recordEvent() {
|
||||
record('recordEvent');
|
||||
record('api/dvr/entry/create_by_event');
|
||||
}
|
||||
|
||||
function recordSeries() {
|
||||
record('recordSeries');
|
||||
record('api/dvr/autorec/create_by_series');
|
||||
}
|
||||
|
||||
function record(op) {
|
||||
function record(url) {
|
||||
Ext.Ajax.request({
|
||||
url: 'dvr',
|
||||
url: url,
|
||||
params: {
|
||||
eventId: event.id,
|
||||
op: op,
|
||||
config_name: confcombo.getValue()
|
||||
event_id: event.id,
|
||||
config_uuid: confcombo.getValue()
|
||||
},
|
||||
success: function(response, options) {
|
||||
win.close();
|
||||
|
@ -232,7 +255,7 @@ tvheadend.epg = function() {
|
|||
}, {
|
||||
name: 'agerating'
|
||||
}, {
|
||||
name: 'contenttype'
|
||||
name: 'content_type'
|
||||
}, {
|
||||
name: 'schedstate'
|
||||
}, {
|
||||
|
@ -244,7 +267,7 @@ tvheadend.epg = function() {
|
|||
var now = new Date;
|
||||
var start = record.get('start');
|
||||
|
||||
if (now.getTime() > start.getTime()) {
|
||||
if (now.getTime() >= start.getTime()) {
|
||||
meta.attr = 'style="font-weight:bold;"';
|
||||
}
|
||||
}
|
||||
|
@ -302,7 +325,7 @@ tvheadend.epg = function() {
|
|||
var now = new Date();
|
||||
|
||||
// Only render a progress bar for currently running programmes
|
||||
if (now <= end && now >= start)
|
||||
if (now >= start)
|
||||
return (now - start) / 1000 / duration * 100;
|
||||
else
|
||||
return "";
|
||||
|
@ -364,9 +387,9 @@ tvheadend.epg = function() {
|
|||
renderer: renderInt
|
||||
}, {
|
||||
width: 250,
|
||||
id: 'contenttype',
|
||||
id: 'content_type',
|
||||
header: "Content Type",
|
||||
dataIndex: 'contenttype',
|
||||
dataIndex: 'content_type',
|
||||
renderer: function(v) {
|
||||
return tvheadend.contentGroupLookupName(v);
|
||||
}
|
||||
|
@ -490,12 +513,12 @@ tvheadend.epg = function() {
|
|||
};
|
||||
|
||||
clearContentGroupFilter = function() {
|
||||
delete epgStore.baseParams.contenttype;
|
||||
delete epgStore.baseParams.content_type;
|
||||
epgFilterContentGroup.setValue("");
|
||||
};
|
||||
|
||||
clearDurationFilter = function() {
|
||||
delete epgStore.baseParams.minduration;
|
||||
delete epgStore.baseParams.minduration;
|
||||
delete epgStore.baseParams.maxduration;
|
||||
epgFilterDuration.setValue("");
|
||||
};
|
||||
|
@ -532,8 +555,8 @@ tvheadend.epg = function() {
|
|||
epgFilterContentGroup.on('select', function(c, r) {
|
||||
if (r.data.code == -1)
|
||||
clearContentGroupFilter();
|
||||
else if (epgStore.baseParams.contenttype !== r.data.code)
|
||||
epgStore.baseParams.contenttype = r.data.code;
|
||||
else if (epgStore.baseParams.content_type !== r.data.code)
|
||||
epgStore.baseParams.content_type = r.data.code;
|
||||
epgStore.reload();
|
||||
});
|
||||
|
||||
|
@ -566,6 +589,44 @@ tvheadend.epg = function() {
|
|||
}
|
||||
});
|
||||
|
||||
tvheadend.autorecButton = new Ext.Button({
|
||||
text: 'Create AutoRec',
|
||||
iconCls: 'wand',
|
||||
tooltip: 'Create an automatic recording entry that will '
|
||||
+ 'record all future programmes that matches '
|
||||
+ 'the current query.',
|
||||
handler: createAutoRec
|
||||
});
|
||||
|
||||
var tbar = [
|
||||
epgFilterTitle, '-',
|
||||
epgFilterChannels, '-',
|
||||
epgFilterChannelTags, '-',
|
||||
epgFilterContentGroup, '-',
|
||||
epgFilterDuration, '-',
|
||||
{
|
||||
text: 'Reset All',
|
||||
handler: epgQueryClear
|
||||
},
|
||||
'->',
|
||||
{
|
||||
text: 'Watch TV',
|
||||
iconCls: 'eye',
|
||||
handler: function() {
|
||||
new tvheadend.VideoPlayer();
|
||||
}
|
||||
},
|
||||
'-',
|
||||
tvheadend.autorecButton,
|
||||
'-',
|
||||
{
|
||||
text: 'Help',
|
||||
handler: function() {
|
||||
new tvheadend.help('Electronic Program Guide', 'epg.html');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var panel = new Ext.ux.grid.livegrid.GridPanel({
|
||||
stateful: true,
|
||||
stateId: 'epggrid',
|
||||
|
@ -577,43 +638,7 @@ tvheadend.epg = function() {
|
|||
store: epgStore,
|
||||
selModel: new Ext.ux.grid.livegrid.RowSelectionModel(),
|
||||
view: epgView,
|
||||
tbar: [
|
||||
epgFilterTitle,
|
||||
'-',
|
||||
epgFilterChannels,
|
||||
'-',
|
||||
epgFilterChannelTags,
|
||||
'-',
|
||||
epgFilterContentGroup,
|
||||
'-',
|
||||
epgFilterDuration,
|
||||
'-',
|
||||
{
|
||||
text: 'Reset All',
|
||||
handler: epgQueryClear
|
||||
},
|
||||
'->',
|
||||
{
|
||||
text: 'Watch TV',
|
||||
iconCls: 'eye',
|
||||
handler: function() {
|
||||
new tvheadend.VideoPlayer();
|
||||
}
|
||||
},
|
||||
'-',
|
||||
{
|
||||
text: 'Create AutoRec',
|
||||
iconCls: 'wand',
|
||||
tooltip: 'Create an automatic recording entry that will '
|
||||
+ 'record all future programmes that matches '
|
||||
+ 'the current query.',
|
||||
handler: createAutoRec
|
||||
}, '-', {
|
||||
text: 'Help',
|
||||
handler: function() {
|
||||
new tvheadend.help('Electronic Program Guide', 'epg.html');
|
||||
}
|
||||
}],
|
||||
tbar: tbar,
|
||||
bbar: new Ext.ux.grid.livegrid.Toolbar({
|
||||
view: epgView,
|
||||
displayInfo: true
|
||||
|
@ -643,6 +668,9 @@ tvheadend.epg = function() {
|
|||
}
|
||||
|
||||
function createAutoRec() {
|
||||
|
||||
if (!tvheadend.accessUpdate.dvr)
|
||||
return;
|
||||
|
||||
var title = epgStore.baseParams.title ? epgStore.baseParams.title
|
||||
: "<i>Don't care</i>";
|
||||
|
@ -650,19 +678,18 @@ tvheadend.epg = function() {
|
|||
: "<i>Don't care</i>";
|
||||
var tag = epgStore.baseParams.tag ? tvheadend.tagLookupName(epgStore.baseParams.tag)
|
||||
: "<i>Don't care</i>";
|
||||
var contenttype = epgStore.baseParams.contenttype ? tvheadend.contentGroupLookupName(epgStore.baseParams.contenttype)
|
||||
var content_type = epgStore.baseParams.content_type ? tvheadend.contentGroupLookupName(epgStore.baseParams.content_type)
|
||||
: "<i>Don't care</i>";
|
||||
var duration = epgStore.baseParams.minduration ? tvheadend.durationLookupRange(epgStore.baseParams.minduration)
|
||||
: "<i>Don't care</i>";
|
||||
|
||||
|
||||
Ext.MessageBox.confirm('Auto Recorder', 'This will create an automatic rule that '
|
||||
+ 'continuously scans the EPG for programmes '
|
||||
+ 'to record that match this query: ' + '<br><br>'
|
||||
+ '<div class="x-smallhdr">Title:</div>' + title + '<br>'
|
||||
+ '<div class="x-smallhdr">Channel:</div>' + channel + '<br>'
|
||||
+ '<div class="x-smallhdr">Tag:</div>' + tag + '<br>'
|
||||
+ '<div class="x-smallhdr">Genre:</div>' + contenttype + '<br>'
|
||||
+ '<div class="x-smallhdr">Genre:</div>' + content_type + '<br>'
|
||||
+ '<div class="x-smallhdr">Duration:</div>' + duration + '<br>'
|
||||
+ '<br><br>' + 'Currently this will match (and record) '
|
||||
+ epgStore.getTotalCount() + ' events. ' + 'Are you sure?',
|
||||
|
@ -675,10 +702,19 @@ tvheadend.epg = function() {
|
|||
|
||||
function createAutoRec2(params) {
|
||||
/* Really do it */
|
||||
params.op = 'createAutoRec';
|
||||
var conf = {
|
||||
enabled: 1,
|
||||
comment: 'Created from EPG query',
|
||||
};
|
||||
if (params.title) conf.title = params.title;
|
||||
if (params.channel) conf.channel = params.channel;
|
||||
if (params.tag) conf.tag = params.tag;
|
||||
if (params.content_type) conf.content_type = params.content_type;
|
||||
if (params.minduration) conf.minduration = params.minduration;
|
||||
if (params.maxduration) conf.maxduration = params.maxduration;
|
||||
Ext.Ajax.request({
|
||||
url: 'dvr',
|
||||
params: params
|
||||
url: 'api/dvr/autorec/create',
|
||||
params: { conf: Ext.encode(conf) }
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ tvheadend.epggrabChannels = new Ext.data.JsonStore({
|
|||
'mod-name']
|
||||
});
|
||||
|
||||
tvheadend.epggrab = function() {
|
||||
tvheadend.epggrab = function(panel, index) {
|
||||
|
||||
/* ****************************************************************
|
||||
* Data
|
||||
|
@ -394,5 +394,5 @@ tvheadend.epggrab = function() {
|
|||
});
|
||||
}
|
||||
|
||||
return confpanel;
|
||||
tvheadend.paneladd(panel, confpanel, index);
|
||||
};
|
||||
|
|
|
@ -6,7 +6,6 @@ tvheadend.esfilter_tab = function(panel)
|
|||
{
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/esfilter/video',
|
||||
comet: 'esfilter_video',
|
||||
titleS: 'Video Stream Filter',
|
||||
titleP: 'Video Stream Filters',
|
||||
tabIndex: 0,
|
||||
|
@ -23,7 +22,6 @@ tvheadend.esfilter_tab = function(panel)
|
|||
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/esfilter/audio',
|
||||
comet: 'esfilter_audio',
|
||||
titleS: 'Audio Stream Filter',
|
||||
titleP: 'Audio Stream Filters',
|
||||
tabIndex: 1,
|
||||
|
@ -40,7 +38,6 @@ tvheadend.esfilter_tab = function(panel)
|
|||
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/esfilter/teletext',
|
||||
comet: 'esfilter_teletext',
|
||||
titleS: 'Teletext Stream Filter',
|
||||
titleP: 'Teletext Stream Filters',
|
||||
tabIndex: 2,
|
||||
|
@ -57,7 +54,6 @@ tvheadend.esfilter_tab = function(panel)
|
|||
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/esfilter/subtit',
|
||||
comet: 'esfilter_subtit',
|
||||
titleS: 'Subtitle Stream Filter',
|
||||
titleP: 'Subtitle Stream Filters',
|
||||
tabIndex: 3,
|
||||
|
@ -74,7 +70,6 @@ tvheadend.esfilter_tab = function(panel)
|
|||
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/esfilter/ca',
|
||||
comet: 'esfilter_ca',
|
||||
titleS: 'CA Stream Filter',
|
||||
titleP: 'CA Stream Filters',
|
||||
tabIndex: 4,
|
||||
|
@ -91,7 +86,6 @@ tvheadend.esfilter_tab = function(panel)
|
|||
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/esfilter/other',
|
||||
comet: 'esfilter_other',
|
||||
titleS: 'Other Stream Filter',
|
||||
titleP: 'Other Stream Filters',
|
||||
tabIndex: 5,
|
||||
|
|
|
@ -24,6 +24,24 @@
|
|||
text-shadow: 0 -2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
|
||||
.x-tab-strip li.x-tab-login {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.x-tab-strip span.x-tab-strip-login {
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
padding:4px 0px;
|
||||
}
|
||||
|
||||
.x-tab-strip span.x-tab-strip-login-cmd {
|
||||
vertical-align: middle;
|
||||
cursor:pointer;
|
||||
padding:4px 0;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.x-tree-col {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
|
@ -148,6 +166,10 @@
|
|||
background-image: url(../icons/delete.png) !important;
|
||||
}
|
||||
|
||||
.cancel {
|
||||
background-image: url(../icons/cancel.png) !important;
|
||||
}
|
||||
|
||||
.moveup {
|
||||
background-image: url(../icons/arrow_up.png) !important;
|
||||
}
|
||||
|
@ -294,7 +316,6 @@
|
|||
|
||||
.arrow_switch {
|
||||
background-image: url(../icons/arrow_switch.png) !important;
|
||||
|
||||
}
|
||||
|
||||
.stream_config {
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Ext JS Library 2.2
|
||||
* Copyright(c) 2006-2008, Ext JS, LLC.
|
||||
|
@ -1237,3 +1235,782 @@ Ext.ux.form.LovCombo = Ext.extend(Ext.form.ComboBox, {
|
|||
|
||||
// register xtype
|
||||
Ext.reg('lovcombo', Ext.ux.form.LovCombo);
|
||||
|
||||
/**
|
||||
* @class Ext.ux.form.TwinDateTimeField
|
||||
* @extends Ext.form.Field
|
||||
*
|
||||
* DateTime field, combination of DateField and TimeField
|
||||
*
|
||||
* @author Ing. Jozef Sakáloš
|
||||
* @copyright (c) 2008, Ing. Jozef Sakáloš
|
||||
* @version 2.0
|
||||
* @revision $Id: Ext.ux.form.TwinDateTimeField.js 813 2010-01-29 23:32:36Z jozo $
|
||||
*
|
||||
* @license Ext.ux.form.TwinDateTimeField is licensed under the terms of the Open Source
|
||||
* LGPL 3.0 license. Commercial use is permitted to the extent that the
|
||||
* code/component(s) do NOT become part of another Open Source or
|
||||
* Commercially licensed development library or toolkit without
|
||||
* explicit permission.
|
||||
*
|
||||
* <p>
|
||||
* License details: <a href="http://www.gnu.org/licenses/lgpl.html"
|
||||
* target="_blank">http://www.gnu.org/licenses/lgpl.html</a>
|
||||
* </p>
|
||||
*/
|
||||
|
||||
Ext.ns('Ext.ux.form');
|
||||
|
||||
// register xtype
|
||||
//Ext.reg('twindatetime', Ext.ux.form.TwinDateTimeField);
|
||||
//Ext.namespace('Ext.ux.form');
|
||||
Ext.ux.form.TwinDateField = Ext.extend(Ext.form.DateField, {
|
||||
getTrigger : Ext.form.TwinTriggerField.prototype.getTrigger,
|
||||
initTrigger : Ext.form.TwinTriggerField.prototype.initTrigger,
|
||||
initComponent : Ext.form.TwinTriggerField.prototype.initComponent,
|
||||
trigger2Class : 'x-form-date-trigger',
|
||||
trigger1Class : 'x-form-clear-trigger',
|
||||
hideTrigger1 : true,
|
||||
submitOnSelect : true,
|
||||
submitOnClear : true,
|
||||
allowClear : true,
|
||||
defaultValue : null,
|
||||
|
||||
onSelect : Ext.form.DateField.prototype.onSelect.createSequence(function(v) {
|
||||
if (this.value && this.ownerCt && this.ownerCt.buttons && this.submitOnSelect) {
|
||||
this.ownerCt.buttons[0].handler.call(this.ownerCt);
|
||||
}
|
||||
}),
|
||||
|
||||
onRender : Ext.form.DateField.prototype.onRender.createSequence(function(v) {
|
||||
this.getTrigger(0).hide();
|
||||
}),
|
||||
|
||||
setValue : Ext.form.DateField.prototype.setValue.createSequence(function(v) {
|
||||
if (v !== null && v != '') {
|
||||
if (this.allowClear)
|
||||
this.getTrigger(0).show();
|
||||
} else {
|
||||
this.getTrigger(0).hide();
|
||||
}
|
||||
}),
|
||||
|
||||
reset : Ext.form.DateField.prototype.reset.createSequence(function() {
|
||||
this.originalValue = this.defaultValue;
|
||||
this.setValue(this.defaultValue);
|
||||
if (this.allowClear)
|
||||
this.getTrigger(0).hide();
|
||||
}),
|
||||
|
||||
onTrigger2Click : function() {
|
||||
if (!this.readOnly)
|
||||
this.onTriggerClick();
|
||||
},
|
||||
|
||||
onTrigger1Click : function() {
|
||||
if (!this.disabled && !this.readOnly) {
|
||||
this.clearValue();
|
||||
this.getTrigger(0).hide();
|
||||
if (this.ownerCt && this.ownerCt.buttons && this.submitOnClear) {
|
||||
this.ownerCt.buttons[0].handler.call(this.ownerCt);
|
||||
}
|
||||
this.fireEvent('clear', this);
|
||||
this.onFocus();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears any text/value currently set in the field
|
||||
*/
|
||||
clearValue : function() {
|
||||
if (this.hiddenField) {
|
||||
this.hiddenField.value = '';
|
||||
}
|
||||
this.setRawValue('');
|
||||
this.lastSelectionText = '';
|
||||
this.applyEmptyText();
|
||||
this.value = '';
|
||||
}
|
||||
});
|
||||
//Ext.ComponentMgr.registerType('twindatefield', Ext.ux.form.TwinDateField);
|
||||
|
||||
|
||||
/**
|
||||
* Creates new DateTime
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object}
|
||||
* config A config object
|
||||
*/
|
||||
Ext.ux.form.TwinDateTimeField = Ext.extend(Ext.form.Field, {
|
||||
/**
|
||||
* @cfg {Function} dateValidator A custom validation function to be called
|
||||
* during date field validation (defaults to null)
|
||||
*/
|
||||
dateValidator : null,
|
||||
|
||||
/**
|
||||
* @cfg {String/Object} defaultAutoCreate DomHelper element spec Let
|
||||
* superclass to create hidden field instead of textbox. Hidden will be
|
||||
* submittend to server
|
||||
*/
|
||||
defaultAutoCreate : {
|
||||
tag : 'input',
|
||||
type : 'hidden'
|
||||
},
|
||||
|
||||
/**
|
||||
* @cfg {String} dtSeparator Date - Time separator. Used to split date and
|
||||
* time (defaults to ' ' (space))
|
||||
*/
|
||||
dtSeparator : ' ',
|
||||
|
||||
/**
|
||||
* @cfg {String} hiddenFormat Format of datetime used to store value in hidden
|
||||
* field and submitted to server (defaults to 'Y-m-d H:i:s' that is mysql
|
||||
* format)
|
||||
*/
|
||||
hiddenFormat : 'Y-m-d H:i:s',
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} otherToNow Set other field to now() if not explicly filled
|
||||
* in (defaults to true)
|
||||
*/
|
||||
otherToNow : true,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} emptyToNow Set field value to now on attempt to set empty
|
||||
* value. If it is true then setValue() sets value of field to current
|
||||
* date and time (defaults to false)
|
||||
*/
|
||||
/**
|
||||
* @cfg {String} timePosition Where the time field should be rendered. 'right'
|
||||
* is suitable for forms and 'below' is suitable if the field is used as
|
||||
* the grid editor (defaults to 'right')
|
||||
*/
|
||||
timePosition : 'right', // valid values:'below', 'right'
|
||||
|
||||
/**
|
||||
* @cfg {Function} timeValidator A custom validation function to be called
|
||||
* during time field validation (defaults to null)
|
||||
*/
|
||||
timeValidator : null,
|
||||
|
||||
/**
|
||||
* @cfg {Number} timeWidth Width of time field in pixels (defaults to 100)
|
||||
*/
|
||||
timeWidth : 100,
|
||||
|
||||
/**
|
||||
* @cfg {String} dateFormat Format of DateField. Can be localized. (defaults
|
||||
* to 'm/y/d')
|
||||
*/
|
||||
dateFormat : 'm/d/Y',
|
||||
|
||||
/**
|
||||
* @cfg {String} timeFormat Format of TimeField. Can be localized. (defaults
|
||||
* to 'g:i A')
|
||||
*/
|
||||
timeFormat : 'g:i A',
|
||||
|
||||
/**
|
||||
* @cfg {Object} dateConfig Config for DateField constructor.
|
||||
*/
|
||||
/**
|
||||
* @cfg {Object} timeConfig Config for TimeField constructor.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @private creates DateField and TimeField and installs the necessary event
|
||||
* handlers
|
||||
*/
|
||||
initComponent : function() {
|
||||
// call parent initComponent
|
||||
Ext.ux.form.TwinDateTimeField.superclass.initComponent.call(this);
|
||||
|
||||
if (this.value) this.value = this.value * 1000;
|
||||
|
||||
// create DateField
|
||||
var dateConfig = Ext.apply({}, {
|
||||
id : this.id + '-date',
|
||||
format : this.dateFormat || Ext.ux.form.TwinDateField.prototype.format,
|
||||
width : this.timeWidth,
|
||||
selectOnFocus : this.selectOnFocus,
|
||||
validator : this.dateValidator,
|
||||
listeners : {
|
||||
blur : {
|
||||
scope : this,
|
||||
fn : this.onBlur
|
||||
},
|
||||
focus : {
|
||||
scope : this,
|
||||
fn : this.onFocus
|
||||
}
|
||||
}
|
||||
}, this.dateConfig);
|
||||
this.df = new Ext.ux.form.TwinDateField(dateConfig);
|
||||
this.df.ownerCt = this;
|
||||
delete(this.dateFormat);
|
||||
|
||||
// create TimeField
|
||||
var timeConfig = Ext.apply({}, {
|
||||
id : this.id + '-time',
|
||||
format : this.timeFormat || Ext.form.TimeField.prototype.format,
|
||||
width : this.timeWidth,
|
||||
selectOnFocus : this.selectOnFocus,
|
||||
validator : this.timeValidator,
|
||||
listeners : {
|
||||
blur : {
|
||||
scope : this,
|
||||
fn : this.onBlur
|
||||
},
|
||||
focus : {
|
||||
scope : this,
|
||||
fn : this.onFocus
|
||||
}
|
||||
}
|
||||
}, this.timeConfig);
|
||||
this.tf = new Ext.form.TimeField(timeConfig);
|
||||
this.tf.ownerCt = this;
|
||||
delete(this.timeFormat);
|
||||
|
||||
// relay events
|
||||
this.relayEvents(this.df, ['focus', 'specialkey', 'invalid', 'valid']);
|
||||
this.relayEvents(this.tf, ['focus', 'specialkey', 'invalid', 'valid']);
|
||||
|
||||
this.on('specialkey', this.onSpecialKey, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Renders underlying DateField and TimeField and provides a
|
||||
* workaround for side error icon bug
|
||||
*/
|
||||
onRender : function(ct, position) {
|
||||
// don't run more than once
|
||||
if (this.isRendered) {
|
||||
return;
|
||||
}
|
||||
|
||||
// render underlying hidden field
|
||||
Ext.ux.form.TwinDateTimeField.superclass.onRender.call(this, ct, position);
|
||||
|
||||
// render DateField and TimeField
|
||||
// create bounding table
|
||||
var t;
|
||||
if ('below' === this.timePosition || 'bellow' === this.timePosition) {
|
||||
t = Ext.DomHelper.append(ct, {
|
||||
tag : 'table',
|
||||
style : 'border-collapse:collapse',
|
||||
children : [{
|
||||
tag : 'tr',
|
||||
children : [{
|
||||
tag : 'td',
|
||||
style : 'padding-bottom:1px',
|
||||
cls : 'ux-datetime-date'
|
||||
}]
|
||||
}, {
|
||||
tag : 'tr',
|
||||
children : [{
|
||||
tag : 'td',
|
||||
cls : 'ux-datetime-time'
|
||||
}]
|
||||
}]
|
||||
}, true);
|
||||
} else {
|
||||
t = Ext.DomHelper.append(ct, {
|
||||
tag : 'table',
|
||||
style : 'border-collapse:collapse',
|
||||
children : [{
|
||||
tag : 'tr',
|
||||
children : [{
|
||||
tag : 'td',
|
||||
style : 'padding-right:4px',
|
||||
cls : 'ux-datetime-date'
|
||||
}, {
|
||||
tag : 'td',
|
||||
cls : 'ux-datetime-time'
|
||||
}]
|
||||
}]
|
||||
}, true);
|
||||
}
|
||||
|
||||
this.tableEl = t;
|
||||
this.wrap = t.wrap({
|
||||
cls : 'x-form-field-wrap'
|
||||
});
|
||||
// this.wrap = t.wrap();
|
||||
this.wrap.on("mousedown", this.onMouseDown, this, {
|
||||
delay : 10
|
||||
});
|
||||
|
||||
// render DateField & TimeField
|
||||
this.df.render(t.child('td.ux-datetime-date'));
|
||||
this.tf.render(t.child('td.ux-datetime-time'));
|
||||
|
||||
// workaround for IE trigger misalignment bug
|
||||
// see http://extjs.com/forum/showthread.php?p=341075#post341075
|
||||
// if(Ext.isIE && Ext.isStrict) {
|
||||
// t.select('input').applyStyles({top:0});
|
||||
// }
|
||||
|
||||
this.df.el.swallowEvent(['keydown', 'keypress']);
|
||||
this.tf.el.swallowEvent(['keydown', 'keypress']);
|
||||
|
||||
// create icon for side invalid errorIcon
|
||||
if ('side' === this.msgTarget) {
|
||||
var elp = this.el.findParent('.x-form-element', 10, true);
|
||||
if (elp) {
|
||||
this.errorIcon = elp.createChild({
|
||||
cls : 'x-form-invalid-icon'
|
||||
});
|
||||
}
|
||||
|
||||
var o = {
|
||||
errorIcon : this.errorIcon,
|
||||
msgTarget : 'side',
|
||||
alignErrorIcon : this.alignErrorIcon.createDelegate(this)
|
||||
};
|
||||
Ext.apply(this.df, o);
|
||||
Ext.apply(this.tf, o);
|
||||
// this.df.errorIcon = this.errorIcon;
|
||||
// this.tf.errorIcon = this.errorIcon;
|
||||
}
|
||||
|
||||
// setup name for submit
|
||||
this.el.dom.name = this.hiddenName || this.name || this.id;
|
||||
|
||||
// prevent helper fields from being submitted
|
||||
this.df.el.dom.removeAttribute("name");
|
||||
this.tf.el.dom.removeAttribute("name");
|
||||
|
||||
// we're rendered flag
|
||||
this.isRendered = true;
|
||||
|
||||
// update hidden field
|
||||
this.updateHidden();
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
adjustSize : Ext.BoxComponent.prototype.adjustSize,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
alignErrorIcon : function() {
|
||||
this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private initializes internal dateValue
|
||||
*/
|
||||
initDateValue : function() {
|
||||
this.dateValue = this.otherToNow ? new Date() : new Date(1970, 0, 1, 0, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls clearInvalid on the DateField and TimeField
|
||||
*/
|
||||
clearInvalid : function() {
|
||||
this.df.clearInvalid();
|
||||
this.tf.clearInvalid();
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls markInvalid on both DateField and TimeField
|
||||
*
|
||||
* @param {String}
|
||||
* msg Invalid message to display
|
||||
*/
|
||||
markInvalid : function(msg) {
|
||||
this.df.markInvalid(msg);
|
||||
this.tf.markInvalid(msg);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private called from Component::destroy. Destroys all elements and removes
|
||||
* all listeners we've created.
|
||||
*/
|
||||
beforeDestroy : function() {
|
||||
if (this.isRendered) {
|
||||
// this.removeAllListeners();
|
||||
this.wrap.removeAllListeners();
|
||||
this.wrap.remove();
|
||||
this.tableEl.remove();
|
||||
this.df.destroy();
|
||||
this.tf.destroy();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable this component.
|
||||
*
|
||||
* @return {Ext.Component} this
|
||||
*/
|
||||
disable : function() {
|
||||
if (this.isRendered) {
|
||||
this.df.disabled = this.disabled;
|
||||
this.df.onDisable();
|
||||
this.tf.onDisable();
|
||||
}
|
||||
this.disabled = true;
|
||||
this.df.disabled = true;
|
||||
this.tf.disabled = true;
|
||||
this.fireEvent("disable", this);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable this component.
|
||||
*
|
||||
* @return {Ext.Component} this
|
||||
*/
|
||||
enable : function() {
|
||||
if (this.rendered) {
|
||||
this.df.onEnable();
|
||||
this.tf.onEnable();
|
||||
}
|
||||
this.disabled = false;
|
||||
this.df.disabled = false;
|
||||
this.tf.disabled = false;
|
||||
this.fireEvent("enable", this);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Focus date filed
|
||||
*/
|
||||
focus : function() {
|
||||
this.df.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getPositionEl : function() {
|
||||
return this.wrap;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getResizeEl : function() {
|
||||
return this.wrap;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {Date/String} Returns value of this field
|
||||
*/
|
||||
getValue : function() {
|
||||
// create new instance of date
|
||||
return this.dateValue ? parseInt(this.dateValue.getTime() / 1000) : '';
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {Boolean} true = valid, false = invalid
|
||||
* @private Calls isValid methods of underlying DateField and TimeField and
|
||||
* returns the result
|
||||
*/
|
||||
isValid : function() {
|
||||
return this.df.isValid() && this.tf.isValid();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if this component is visible
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isVisible : function() {
|
||||
return this.df.rendered && this.df.getActionEl().isVisible();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Handles blur event
|
||||
*/
|
||||
onBlur : function(f) {
|
||||
// called by both DateField and TimeField blur events
|
||||
|
||||
// revert focus to previous field if clicked in between
|
||||
if (this.wrapClick) {
|
||||
f.focus();
|
||||
this.wrapClick = false;
|
||||
}
|
||||
|
||||
// update underlying value
|
||||
if (f === this.df) {
|
||||
this.updateDate();
|
||||
} else {
|
||||
this.updateTime();
|
||||
}
|
||||
this.updateHidden();
|
||||
|
||||
this.validate();
|
||||
|
||||
// fire events later
|
||||
(function() {
|
||||
if (!this.df.hasFocus && !this.tf.hasFocus) {
|
||||
var v = this.getValue();
|
||||
if (String(v) !== String(this.startValue)) {
|
||||
this.fireEvent("change", this, v, this.startValue);
|
||||
}
|
||||
this.hasFocus = false;
|
||||
this.fireEvent('blur', this);
|
||||
}
|
||||
}).defer(100, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Handles focus event
|
||||
*/
|
||||
onFocus : function() {
|
||||
if (!this.hasFocus) {
|
||||
this.hasFocus = true;
|
||||
this.startValue = this.getValue();
|
||||
this.fireEvent("focus", this);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Just to prevent blur event when clicked in the middle of fields
|
||||
*/
|
||||
onMouseDown : function(e) {
|
||||
if (!this.disabled) {
|
||||
this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Handles Tab and Shift-Tab events
|
||||
*/
|
||||
onSpecialKey : function(t, e) {
|
||||
var key = e.getKey();
|
||||
if (key === e.TAB) {
|
||||
if (t === this.df && !e.shiftKey) {
|
||||
e.stopEvent();
|
||||
this.tf.focus();
|
||||
}
|
||||
if (t === this.tf && e.shiftKey) {
|
||||
e.stopEvent();
|
||||
this.df.focus();
|
||||
}
|
||||
this.updateValue();
|
||||
}
|
||||
// otherwise it misbehaves in editor grid
|
||||
if (key === e.ENTER) {
|
||||
this.updateValue();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Resets the current field value to the originally loaded value and clears
|
||||
* any validation messages. See Ext.form.BasicForm.trackResetOnLoad
|
||||
*/
|
||||
reset : function() {
|
||||
this.df.setValue(this.originalValue);
|
||||
this.tf.setValue(this.originalValue);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Sets the value of DateField
|
||||
*/
|
||||
setDate : function(date) {
|
||||
this.df.setValue(date);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Sets the value of TimeField
|
||||
*/
|
||||
setTime : function(date) {
|
||||
this.tf.setValue(date);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Sets correct sizes of underlying DateField and TimeField With
|
||||
* workarounds for IE bugs
|
||||
*/
|
||||
setSize : function(w, h) {
|
||||
if (!w) {
|
||||
return;
|
||||
}
|
||||
if ('below' === this.timePosition) {
|
||||
this.df.setSize(w, h);
|
||||
this.tf.setSize(w, h);
|
||||
if (Ext.isIE) {
|
||||
this.df.el.up('td').setWidth(w);
|
||||
this.tf.el.up('td').setWidth(w);
|
||||
}
|
||||
} else {
|
||||
this.df.setSize(w - this.timeWidth - 4, h);
|
||||
this.tf.setSize(this.timeWidth, h);
|
||||
|
||||
if (Ext.isIE) {
|
||||
this.df.el.up('td').setWidth(w - this.timeWidth - 4);
|
||||
this.tf.el.up('td').setWidth(this.timeWidth);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Mixed}
|
||||
* val Value to set Sets the value of this field
|
||||
*/
|
||||
setValue : function(val) {
|
||||
if (!val && true === this.emptyToNow) {
|
||||
this.setValue(new Date());
|
||||
return;
|
||||
} else if (!val) {
|
||||
this.setDate('');
|
||||
this.setTime('');
|
||||
this.updateValue();
|
||||
return;
|
||||
}
|
||||
if ('number' === typeof val) {
|
||||
val = new Date(val);
|
||||
} else if ('string' === typeof val && this.hiddenFormat) {
|
||||
val = Date.parseDate(val, this.hiddenFormat);
|
||||
}
|
||||
val = val ? val : new Date(1970, 0, 1, 0, 0, 0);
|
||||
var da;
|
||||
if (val instanceof Date) {
|
||||
this.setDate(val);
|
||||
this.setTime(val);
|
||||
this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
|
||||
} else {
|
||||
da = val.split(this.dtSeparator);
|
||||
this.setDate(da[0]);
|
||||
if (da[1]) {
|
||||
if (da[2]) {
|
||||
// add am/pm part back to time
|
||||
da[1] += da[2];
|
||||
}
|
||||
this.setTime(da[1]);
|
||||
}
|
||||
}
|
||||
this.updateValue();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide or show this component by boolean
|
||||
*
|
||||
* @return {Ext.Component} this
|
||||
*/
|
||||
setVisible : function(visible) {
|
||||
if (visible) {
|
||||
this.df.show();
|
||||
this.tf.show();
|
||||
} else {
|
||||
this.df.hide();
|
||||
this.tf.hide();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
show : function() {
|
||||
return this.setVisible(true);
|
||||
},
|
||||
|
||||
hide : function() {
|
||||
return this.setVisible(false);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Updates the date part
|
||||
*/
|
||||
updateDate : function() {
|
||||
var d = this.df.getValue();
|
||||
if (d) {
|
||||
if (!(this.dateValue instanceof Date)) {
|
||||
this.initDateValue();
|
||||
if (!this.tf.getValue()) {
|
||||
this.setTime(this.dateValue);
|
||||
}
|
||||
}
|
||||
this.dateValue.setMonth(0); // because of leap years
|
||||
this.dateValue.setFullYear(d.getFullYear());
|
||||
this.dateValue.setMonth(d.getMonth(), d.getDate());
|
||||
// this.dateValue.setDate(d.getDate());
|
||||
} else {
|
||||
this.dateValue = '';
|
||||
this.setTime('');
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @private Updates the time part
|
||||
*/
|
||||
updateTime : function() {
|
||||
var t = this.tf.getValue();
|
||||
if (t && !(t instanceof Date)) {
|
||||
t = Date.parseDate(t, this.tf.format);
|
||||
}
|
||||
if (t && !this.df.getValue()) {
|
||||
this.initDateValue();
|
||||
this.setDate(this.dateValue);
|
||||
}
|
||||
if (this.dateValue instanceof Date) {
|
||||
if (t) {
|
||||
this.dateValue.setHours(t.getHours());
|
||||
this.dateValue.setMinutes(t.getMinutes());
|
||||
this.dateValue.setSeconds(t.getSeconds());
|
||||
} else {
|
||||
this.dateValue.setHours(0);
|
||||
this.dateValue.setMinutes(0);
|
||||
this.dateValue.setSeconds(0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @private Updates the underlying hidden field value
|
||||
*/
|
||||
updateHidden : function() {
|
||||
if (this.isRendered) {
|
||||
var value = this.dateValue instanceof Date ? this.dateValue.format(this.hiddenFormat) : '';
|
||||
this.el.dom.value = value;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @private Updates all of Date, Time and Hidden
|
||||
*/
|
||||
updateValue : function() {
|
||||
|
||||
this.updateDate();
|
||||
this.updateTime();
|
||||
this.updateHidden();
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {Boolean} true = valid, false = invalid calls validate methods of
|
||||
* DateField and TimeField
|
||||
*/
|
||||
validate : function() {
|
||||
return this.df.validate() && this.tf.validate();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Returns renderer suitable to render this field
|
||||
*
|
||||
* @param {Object}
|
||||
* Column model config
|
||||
*/
|
||||
renderer : function(field) {
|
||||
var format = field.editor.dateFormat || Ext.ux.form.TwinDateTimeField.prototype.dateFormat;
|
||||
format += ' ' + (field.editor.timeFormat || Ext.ux.form.TwinDateTimeField.prototype.timeFormat);
|
||||
var renderer = function(val) {
|
||||
var retval = Ext.util.Format.date(val, format);
|
||||
return retval;
|
||||
};
|
||||
return renderer;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,318 +0,0 @@
|
|||
/**
|
||||
* IPTV service grid
|
||||
*/
|
||||
tvheadend.iptv = function(adapterId) {
|
||||
|
||||
var servicetypeStore = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
id: 'val',
|
||||
url: '/iptv/services',
|
||||
baseParams: {
|
||||
op: 'servicetypeList'
|
||||
},
|
||||
fields: ['val', 'str'],
|
||||
autoLoad: false,
|
||||
sortInfo: {
|
||||
field: 'channelname',
|
||||
direction: 'ASC'
|
||||
}
|
||||
});
|
||||
|
||||
var fm = Ext.form;
|
||||
|
||||
|
||||
var actions = new Ext.ux.grid.RowActions({
|
||||
header: '',
|
||||
dataIndex: 'actions',
|
||||
width: 45,
|
||||
actions: [{
|
||||
iconCls: 'info',
|
||||
qtip: 'Detailed information about service',
|
||||
cb: function(grid, record, action, row, col) {
|
||||
Ext.Ajax.request({
|
||||
url: "servicedetails/" + record.id,
|
||||
success: function(response, options) {
|
||||
r = Ext.util.JSON.decode(response.responseText);
|
||||
tvheadend.showTransportDetails(r);
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
var cm = new Ext.grid.ColumnModel({
|
||||
defaultSortable: true,
|
||||
columns: [
|
||||
{
|
||||
xtype: 'checkcolumn',
|
||||
header: "Enabled",
|
||||
dataIndex: 'enabled',
|
||||
width: 45
|
||||
},
|
||||
{
|
||||
header: "Channel name",
|
||||
dataIndex: 'channelname',
|
||||
width: 150,
|
||||
renderer: function(value, metadata, record, row, col, store) {
|
||||
return value ? value
|
||||
: '<span class="tvh-grid-unset">Unmapped</span>';
|
||||
},
|
||||
editor: new fm.ComboBox({
|
||||
store: tvheadend.channels,
|
||||
allowBlank: true,
|
||||
typeAhead: true,
|
||||
minChars: 2,
|
||||
lazyRender: true,
|
||||
triggerAction: 'all',
|
||||
mode: 'local',
|
||||
displayField: 'name'
|
||||
})
|
||||
},
|
||||
{
|
||||
header: "Interface",
|
||||
dataIndex: 'interface',
|
||||
width: 100,
|
||||
renderer: function(value, metadata, record, row, col, store) {
|
||||
return value ? value : '<span class="tvh-grid-unset">Unset</span>';
|
||||
},
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false
|
||||
})
|
||||
},
|
||||
{
|
||||
header: "Group",
|
||||
dataIndex: 'group',
|
||||
width: 100,
|
||||
renderer: function(value, metadata, record, row, col, store) {
|
||||
return value ? value : '<span class="tvh-grid-unset">Unset</span>';
|
||||
},
|
||||
editor: new fm.TextField({
|
||||
allowBlank: false
|
||||
})
|
||||
},
|
||||
{
|
||||
header: "UDP Port",
|
||||
dataIndex: 'port',
|
||||
width: 60,
|
||||
editor: new fm.NumberField({
|
||||
minValue: 1,
|
||||
maxValue: 65535
|
||||
})
|
||||
},
|
||||
{
|
||||
header: "Service ID",
|
||||
dataIndex: 'sid',
|
||||
width: 50,
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
header: 'Service Type',
|
||||
width: 100,
|
||||
dataIndex: 'stype',
|
||||
hidden: true,
|
||||
editor: new fm.ComboBox({
|
||||
valueField: 'val',
|
||||
displayField: 'str',
|
||||
forceSelection: false,
|
||||
editable: false,
|
||||
mode: 'local',
|
||||
triggerAction: 'all',
|
||||
store: servicetypeStore
|
||||
}),
|
||||
renderer: function(value, metadata, record, row, col, store) {
|
||||
var val = value ? servicetypeStore.getById(value) : null;
|
||||
return val ? val.get('str')
|
||||
: '<span class="tvh-grid-unset">Unset</span>';
|
||||
}
|
||||
}, {
|
||||
header: "PMT PID",
|
||||
dataIndex: 'pmt',
|
||||
width: 50,
|
||||
hidden: true
|
||||
}, {
|
||||
header: "PCR PID",
|
||||
dataIndex: 'pcr',
|
||||
width: 50,
|
||||
hidden: true
|
||||
}, actions]});
|
||||
|
||||
var rec = Ext.data.Record.create(['id', 'enabled', 'channelname',
|
||||
'interface', 'group', 'port', 'sid', 'pmt', 'pcr', 'stype']);
|
||||
|
||||
var store = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
fields: rec,
|
||||
url: "iptv/services",
|
||||
autoLoad: true,
|
||||
id: 'id',
|
||||
baseParams: {
|
||||
op: "get"
|
||||
},
|
||||
listeners: {
|
||||
'update': function(s, r, o) {
|
||||
d = s.getModifiedRecords().length === 0
|
||||
saveBtn.setDisabled(d);
|
||||
rejectBtn.setDisabled(d);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
var storeReloader = new Ext.util.DelayedTask(function() {
|
||||
store.reload()
|
||||
});
|
||||
|
||||
tvheadend.comet.on('dvbService', function(m) {
|
||||
storeReloader.delay(500);
|
||||
});
|
||||
*/
|
||||
|
||||
function addRecord() {
|
||||
Ext.Ajax.request({
|
||||
url: "iptv/services",
|
||||
params: {
|
||||
op: "create"
|
||||
},
|
||||
failure: function(response, options) {
|
||||
Ext.MessageBox.alert('Server Error',
|
||||
'Unable to generate new record');
|
||||
},
|
||||
success: function(response, options) {
|
||||
var responseData = Ext.util.JSON.decode(response.responseText);
|
||||
var p = new rec(responseData, responseData.id);
|
||||
grid.stopEditing();
|
||||
store.insert(0, p);
|
||||
grid.startEditing(0, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
;
|
||||
|
||||
function delSelected() {
|
||||
var selectedKeys = grid.selModel.selections.keys;
|
||||
if (selectedKeys.length > 0) {
|
||||
Ext.MessageBox.confirm('Message',
|
||||
'Do you really want to delete selection?', deleteRecord);
|
||||
}
|
||||
else {
|
||||
Ext.MessageBox.alert('Message',
|
||||
'Please select at least one item to delete');
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
function deleteRecord(btn) {
|
||||
if (btn === 'yes') {
|
||||
var selectedKeys = grid.selModel.selections.keys;
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: "iptv/services",
|
||||
params: {
|
||||
op: "delete",
|
||||
entries: Ext.encode(selectedKeys)
|
||||
},
|
||||
failure: function(response, options) {
|
||||
Ext.MessageBox.alert('Server Error', 'Unable to delete');
|
||||
},
|
||||
success: function(response, options) {
|
||||
store.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function saveChanges() {
|
||||
var mr = store.getModifiedRecords();
|
||||
var out = new Array();
|
||||
for (var x = 0; x < mr.length; x++) {
|
||||
v = mr[x].getChanges();
|
||||
out[x] = v;
|
||||
out[x].id = mr[x].id;
|
||||
}
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: "iptv/services",
|
||||
params: {
|
||||
op: "update",
|
||||
entries: Ext.encode(out)
|
||||
},
|
||||
success: function(response, options) {
|
||||
store.commitChanges();
|
||||
},
|
||||
failure: function(response, options) {
|
||||
Ext.MessageBox.alert('Message', response.statusText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var delButton = new Ext.Toolbar.Button({
|
||||
tooltip: 'Delete one or more selected rows',
|
||||
iconCls: 'remove',
|
||||
text: 'Delete selected services',
|
||||
handler: delSelected,
|
||||
disabled: true
|
||||
});
|
||||
|
||||
var saveBtn = new Ext.Toolbar.Button({
|
||||
tooltip: 'Save any changes made (Changed cells have red borders).',
|
||||
iconCls: 'save',
|
||||
text: "Save changes",
|
||||
handler: saveChanges,
|
||||
disabled: true
|
||||
});
|
||||
|
||||
var rejectBtn = new Ext.Toolbar.Button({
|
||||
tooltip: 'Revert any changes made (Changed cells have red borders).',
|
||||
iconCls: 'undo',
|
||||
text: "Revert changes",
|
||||
handler: function() {
|
||||
store.rejectChanges();
|
||||
},
|
||||
disabled: true
|
||||
});
|
||||
|
||||
var selModel = new Ext.grid.RowSelectionModel({
|
||||
singleSelect: false
|
||||
});
|
||||
|
||||
var grid = new Ext.grid.EditorGridPanel({
|
||||
stripeRows: true,
|
||||
title: 'IPTV',
|
||||
iconCls: 'iptv',
|
||||
plugins: [actions],
|
||||
store: store,
|
||||
clicksToEdit: 2,
|
||||
cm: cm,
|
||||
viewConfig: {
|
||||
forceFit: true
|
||||
},
|
||||
selModel: selModel,
|
||||
tbar: [
|
||||
{
|
||||
tooltip: 'Create a new entry on the server. '
|
||||
+ 'The new entry is initially disabled so it must be enabled '
|
||||
+ 'before it start taking effect.',
|
||||
iconCls: 'add',
|
||||
text: 'Add service',
|
||||
handler: addRecord
|
||||
}, '-', delButton, '-', saveBtn, rejectBtn, '->',
|
||||
{
|
||||
text: 'Help',
|
||||
handler: function() {
|
||||
new tvheadend.help('IPTV', 'config_iptv.html');
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
store.on('update', function(s, r, o) {
|
||||
d = s.getModifiedRecords().length === 0;
|
||||
saveBtn.setDisabled(d);
|
||||
rejectBtn.setDisabled(d);
|
||||
});
|
||||
|
||||
selModel.on('selectionchange', function(self) {
|
||||
delButton.setDisabled(self.getCount() === 0);
|
||||
});
|
||||
|
||||
return grid;
|
||||
};
|
|
@ -25,14 +25,13 @@ tvheadend.comet.on('mpegts_network', function() {
|
|||
tvheadend.network_list.reload();
|
||||
});
|
||||
|
||||
tvheadend.networks = function(panel)
|
||||
tvheadend.networks = function(panel, index)
|
||||
{
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/mpegts/network',
|
||||
comet: 'mpegts_network',
|
||||
titleS: 'Network',
|
||||
titleP: 'Networks',
|
||||
tabIndex: 1,
|
||||
tabIndex: index,
|
||||
help: function() {
|
||||
new tvheadend.help('Networks', 'config_networks.html');
|
||||
},
|
||||
|
@ -57,14 +56,13 @@ tvheadend.networks = function(panel)
|
|||
});
|
||||
};
|
||||
|
||||
tvheadend.muxes = function(panel)
|
||||
tvheadend.muxes = function(panel, index)
|
||||
{
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/mpegts/mux',
|
||||
comet: 'mpegts_mux',
|
||||
titleS: 'Mux',
|
||||
titleP: 'Muxes',
|
||||
tabIndex: 2,
|
||||
tabIndex: index,
|
||||
hidemode: true,
|
||||
help: function() {
|
||||
new tvheadend.help('Muxes', 'config_muxes.html');
|
||||
|
@ -195,53 +193,71 @@ tvheadend.show_service_streams = function(data) {
|
|||
win.show();
|
||||
};
|
||||
|
||||
tvheadend.services = function(panel)
|
||||
tvheadend.services = function(panel, index)
|
||||
{
|
||||
var mapButton = new Ext.Toolbar.Button({
|
||||
tooltip: 'Map services to channels',
|
||||
iconCls: 'clone',
|
||||
text: 'Map All',
|
||||
callback: tvheadend.service_mapper,
|
||||
disabled: false
|
||||
});
|
||||
var selected = function(s)
|
||||
{
|
||||
if (s.getCount() > 0)
|
||||
mapButton.setText('Map Selected');
|
||||
else
|
||||
mapButton.setText('Map All');
|
||||
};
|
||||
var actions = new Ext.ux.grid.RowActions({
|
||||
header: 'Details',
|
||||
width: 10,
|
||||
actions: [{
|
||||
iconCls: 'info',
|
||||
qtip: 'Detailed stream info',
|
||||
cb: function(grid, rec, act, row, col) {
|
||||
Ext.Ajax.request({
|
||||
url: 'api/service/streams',
|
||||
params: {
|
||||
uuid: rec.id
|
||||
},
|
||||
success: function(r, o) {
|
||||
var d = Ext.util.JSON.decode(r.responseText);
|
||||
tvheadend.show_service_streams(d);
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
});
|
||||
function builder(conf) {
|
||||
var mapButton = {
|
||||
name: 'map',
|
||||
builder: function() {
|
||||
return new Ext.Toolbar.Button({
|
||||
tooltip: 'Map services to channels',
|
||||
iconCls: 'clone',
|
||||
text: 'Map All',
|
||||
disabled: false
|
||||
});
|
||||
},
|
||||
callback: tvheadend.service_mapper
|
||||
};
|
||||
|
||||
var selected = function(s, abuttons)
|
||||
{
|
||||
if (s.getCount() > 0)
|
||||
abuttons.map.setText('Map Selected');
|
||||
else
|
||||
abuttons.map.setText('Map All');
|
||||
};
|
||||
|
||||
var actions = new Ext.ux.grid.RowActions({
|
||||
header: 'Details',
|
||||
width: 10,
|
||||
actions: [{
|
||||
iconCls: 'info',
|
||||
qtip: 'Detailed stream info',
|
||||
cb: function(grid, rec, act, row, col) {
|
||||
Ext.Ajax.request({
|
||||
url: 'api/service/streams',
|
||||
params: {
|
||||
uuid: rec.id
|
||||
},
|
||||
success: function(r, o) {
|
||||
var d = Ext.util.JSON.decode(r.responseText);
|
||||
tvheadend.show_service_streams(d);
|
||||
}
|
||||
});
|
||||
}
|
||||
}],
|
||||
destroy: function() {
|
||||
}
|
||||
});
|
||||
conf.tbar = [mapButton];
|
||||
conf.selected = selected;
|
||||
conf.lcol[1] = actions;
|
||||
conf.plugins = [actions];
|
||||
}
|
||||
function destroyer(conf) {
|
||||
delete conf.tbar;
|
||||
delete conf.plugins;
|
||||
conf.lcol[1] = {};
|
||||
conf.selected = null;
|
||||
}
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/mpegts/service',
|
||||
comet: 'service',
|
||||
titleS: 'Service',
|
||||
titleP: 'Services',
|
||||
tabIndex: 3,
|
||||
tabIndex: index,
|
||||
hidemode: true,
|
||||
add: false,
|
||||
del: false,
|
||||
selected: selected,
|
||||
tbar: [mapButton],
|
||||
help: function() {
|
||||
new tvheadend.help('Services', 'config_services.html');
|
||||
},
|
||||
|
@ -255,24 +271,26 @@ tvheadend.services = function(panel)
|
|||
"?title=" + encodeURIComponent(title) + "'>Play</a>";
|
||||
}
|
||||
},
|
||||
actions
|
||||
{
|
||||
/* placeholder for actions */
|
||||
}
|
||||
],
|
||||
plugins: [actions],
|
||||
sort: {
|
||||
field: 'svcname',
|
||||
direction: 'ASC'
|
||||
}
|
||||
},
|
||||
builder: builder,
|
||||
destroyer: destroyer
|
||||
});
|
||||
};
|
||||
|
||||
tvheadend.mux_sched = function(panel)
|
||||
tvheadend.mux_sched = function(panel, index)
|
||||
{
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/mpegts/mux_sched',
|
||||
comet: 'mpegts_mux_sched',
|
||||
titleS: 'Mux Scheduler',
|
||||
titleP: 'Mux Schedulers',
|
||||
tabIndex: 4,
|
||||
tabIndex: index,
|
||||
help: function() {
|
||||
new tvheadend.help('Mux Schedulers', 'config_muxsched.html');
|
||||
},
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
|
||||
tvheadend.service_mapper_status_panel = null;
|
||||
|
||||
tvheadend.service_mapper_status = function()
|
||||
tvheadend.service_mapper_status = function(panel, index)
|
||||
{
|
||||
var panel;
|
||||
|
||||
/* Fields */
|
||||
var ok = new Ext.form.Label({
|
||||
fieldLabel: 'Mapped',
|
||||
|
@ -31,7 +29,7 @@ tvheadend.service_mapper_status = function()
|
|||
});
|
||||
|
||||
/* Panel */
|
||||
panel = new Ext.FormPanel({
|
||||
var mpanel = new Ext.FormPanel({
|
||||
method: 'get',
|
||||
title: 'Service Mapper',
|
||||
frame: true,
|
||||
|
@ -72,9 +70,9 @@ tvheadend.service_mapper_status = function()
|
|||
}
|
||||
});
|
||||
|
||||
tvheadend.service_mapper_status_panel = panel;
|
||||
return panel;
|
||||
};
|
||||
tvheadend.service_mapper_status_panel = mpanel;
|
||||
tvheadend.paneladd(panel, mpanel, index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start mapping
|
||||
|
@ -182,4 +180,4 @@ tvheadend.service_mapper = function(t, e, store, select)
|
|||
});
|
||||
|
||||
win.show();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,52 +1,19 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
tvheadend.status_subs = function() {
|
||||
|
||||
tvheadend.subsStore = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
totalProperty: 'totalCount',
|
||||
fields: [{
|
||||
name: 'id'
|
||||
}, {
|
||||
name: 'hostname'
|
||||
}, {
|
||||
name: 'username'
|
||||
}, {
|
||||
name: 'title'
|
||||
}, {
|
||||
name: 'channel'
|
||||
}, {
|
||||
name: 'service'
|
||||
}, {
|
||||
name: 'state'
|
||||
}, {
|
||||
name: 'errors'
|
||||
}, {
|
||||
name: 'in'
|
||||
}, {
|
||||
name: 'out'
|
||||
}, {
|
||||
name: 'start',
|
||||
type: 'date',
|
||||
dateFormat: 'U' /* unix time */
|
||||
}],
|
||||
url: 'api/status/subscriptions',
|
||||
autoLoad: true,
|
||||
id: 'id'
|
||||
});
|
||||
|
||||
|
||||
|
||||
tvheadend.comet.on('subscriptions', function(m) {
|
||||
tvheadend.status_subs = function(panel, index)
|
||||
{
|
||||
var subs = null;
|
||||
var store = null;
|
||||
|
||||
function update(m) {
|
||||
if (m.reload != null)
|
||||
tvheadend.subsStore.reload();
|
||||
store.reload();
|
||||
|
||||
if (m.updateEntry != null) {
|
||||
r = tvheadend.subsStore.getById(m.id);
|
||||
r = store.getById(m.id);
|
||||
if (typeof r === 'undefined') {
|
||||
tvheadend.subsStore.reload();
|
||||
store.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -57,378 +24,495 @@ tvheadend.status_subs = function() {
|
|||
r.data.in = m.in;
|
||||
r.data.out = m.out;
|
||||
|
||||
tvheadend.subsStore.afterEdit(r);
|
||||
tvheadend.subsStore.fireEvent('updated', tvheadend.subsStore, r,
|
||||
Ext.data.Record.COMMIT);
|
||||
store.afterEdit(r);
|
||||
store.fireEvent('updated', store, r, Ext.data.Record.COMMIT);
|
||||
}
|
||||
});
|
||||
|
||||
function renderDate(value) {
|
||||
var dt = new Date(value);
|
||||
return dt.format('D j M H:i');
|
||||
}
|
||||
|
||||
function renderBw(value, item, store) {
|
||||
var txt = parseInt(value / 125);
|
||||
var href = 'javascript:tvheadend.subscription_bw_monitor(' + store.id + ');';
|
||||
return '<a href="' + href + '">' + txt + '</a>';
|
||||
function builder() {
|
||||
if (subs)
|
||||
return;
|
||||
|
||||
store = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
totalProperty: 'totalCount',
|
||||
fields: [
|
||||
{ name: 'id' },
|
||||
{ name: 'hostname' },
|
||||
{ name: 'username' },
|
||||
{ name: 'title' },
|
||||
{ name: 'channel' },
|
||||
{ name: 'service' },
|
||||
{ name: 'state' },
|
||||
{ name: 'errors' },
|
||||
{ name: 'in' },
|
||||
{ name: 'out' },
|
||||
{
|
||||
name: 'start',
|
||||
type: 'date',
|
||||
dateFormat: 'U' /* unix time */
|
||||
}
|
||||
],
|
||||
url: 'api/status/subscriptions',
|
||||
autoLoad: true,
|
||||
id: 'id'
|
||||
});
|
||||
tvheadend.subsStore = store;
|
||||
|
||||
tvheadend.comet.on('subscriptions', update);
|
||||
|
||||
function renderBw(value, item, record) {
|
||||
var txt = parseInt(value / 125);
|
||||
var href = 'javascript:tvheadend.subscription_bw_monitor(' + record.id + ');';
|
||||
return '<a href="' + href + '">' + txt + '</a>';
|
||||
}
|
||||
|
||||
var subsCm = new Ext.grid.ColumnModel([
|
||||
{
|
||||
width: 50,
|
||||
id: 'hostname',
|
||||
header: "Hostname",
|
||||
dataIndex: 'hostname'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
id: 'username',
|
||||
header: "Username",
|
||||
dataIndex: 'username'
|
||||
},
|
||||
{
|
||||
width: 80,
|
||||
id: 'title',
|
||||
header: "Title",
|
||||
dataIndex: 'title'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
id: 'channel',
|
||||
header: "Channel",
|
||||
dataIndex: 'channel'
|
||||
},
|
||||
{
|
||||
width: 200,
|
||||
id: 'service',
|
||||
header: "Service",
|
||||
dataIndex: 'service'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
id: 'start',
|
||||
header: "Start",
|
||||
dataIndex: 'start',
|
||||
renderer: function(v) {
|
||||
var dt = new Date(v);
|
||||
return dt.format('D j M H:i');
|
||||
}
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
id: 'state',
|
||||
header: "State",
|
||||
dataIndex: 'state'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
id: 'errors',
|
||||
header: "Errors",
|
||||
dataIndex: 'errors'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
id: 'in',
|
||||
header: "Input (kb/s)",
|
||||
dataIndex: 'in',
|
||||
renderer: renderBw,
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
id: 'out',
|
||||
header: "Output (kb/s)",
|
||||
dataIndex: 'out',
|
||||
renderer: renderBw
|
||||
}
|
||||
]);
|
||||
|
||||
subs = new Ext.grid.GridPanel({
|
||||
border: false,
|
||||
loadMask: true,
|
||||
stripeRows: true,
|
||||
disableSelection: true,
|
||||
store: store,
|
||||
cm: subsCm,
|
||||
flex: 1,
|
||||
viewConfig: {
|
||||
forceFit: true
|
||||
}
|
||||
});
|
||||
|
||||
dpanel.add(subs);
|
||||
dpanel.doLayout(false, true);
|
||||
}
|
||||
|
||||
function destroyer() {
|
||||
if (subs === null || !tvheadend.dynamic)
|
||||
return;
|
||||
dpanel.removeAll()
|
||||
tvheadend.subsStore = null;
|
||||
store.destroy();
|
||||
store = null;
|
||||
subs = null;
|
||||
}
|
||||
|
||||
var subsCm = new Ext.grid.ColumnModel([{
|
||||
width: 50,
|
||||
id: 'hostname',
|
||||
header: "Hostname",
|
||||
dataIndex: 'hostname'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'username',
|
||||
header: "Username",
|
||||
dataIndex: 'username'
|
||||
}, {
|
||||
width: 80,
|
||||
id: 'title',
|
||||
header: "Title",
|
||||
dataIndex: 'title'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'channel',
|
||||
header: "Channel",
|
||||
dataIndex: 'channel'
|
||||
}, {
|
||||
width: 200,
|
||||
id: 'service',
|
||||
header: "Service",
|
||||
dataIndex: 'service'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'start',
|
||||
header: "Start",
|
||||
dataIndex: 'start',
|
||||
renderer: renderDate
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'state',
|
||||
header: "State",
|
||||
dataIndex: 'state'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'errors',
|
||||
header: "Errors",
|
||||
dataIndex: 'errors'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'in',
|
||||
header: "Input (kb/s)",
|
||||
dataIndex: 'in',
|
||||
renderer: renderBw
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'out',
|
||||
header: "Output (kb/s)",
|
||||
dataIndex: 'out',
|
||||
renderer: renderBw
|
||||
}]);
|
||||
|
||||
var subs = new Ext.grid.GridPanel({
|
||||
var dpanel = new Ext.Panel({
|
||||
border: false,
|
||||
loadMask: true,
|
||||
stripeRows: true,
|
||||
disableSelection: true,
|
||||
header: false,
|
||||
layout: 'fit',
|
||||
title: 'Subscriptions',
|
||||
iconCls: 'eye',
|
||||
store: tvheadend.subsStore,
|
||||
cm: subsCm,
|
||||
flex: 1,
|
||||
viewConfig: {
|
||||
forceFit: true
|
||||
}
|
||||
iconCls: 'eye'
|
||||
});
|
||||
return subs;
|
||||
|
||||
tvheadend.paneladd(panel, dpanel, index);
|
||||
tvheadend.panelreg(panel, dpanel, builder, destroyer);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Streams
|
||||
*/
|
||||
tvheadend.status_streams = function() {
|
||||
tvheadend.status_streams = function(panel, index)
|
||||
{
|
||||
var grid = null;
|
||||
var store = null;
|
||||
|
||||
tvheadend.streamStatusStore = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
totalProperty: 'totalCount',
|
||||
fields: [{
|
||||
name: 'uuid'
|
||||
}, {
|
||||
name: 'input'
|
||||
}, {
|
||||
name: 'username'
|
||||
}, {
|
||||
name: 'stream'
|
||||
}, {
|
||||
name: 'subs'
|
||||
}, {
|
||||
name: 'weight'
|
||||
}, {
|
||||
name: 'signal'
|
||||
}, {
|
||||
name: 'ber'
|
||||
}, {
|
||||
name: 'unc'
|
||||
}, {
|
||||
name: 'snr'
|
||||
}, {
|
||||
name: 'bps'
|
||||
}, {
|
||||
name: 'cc'
|
||||
}, {
|
||||
name: 'te'
|
||||
}, {
|
||||
name: 'signal_scale'
|
||||
}, {
|
||||
name: 'snr_scale'
|
||||
}, {
|
||||
name: 'ec_bit'
|
||||
}, {
|
||||
name: 'tc_bit'
|
||||
}, {
|
||||
name: 'ec_block'
|
||||
}, {
|
||||
name: 'tc_block'
|
||||
}
|
||||
],
|
||||
url: 'api/status/inputs',
|
||||
autoLoad: true,
|
||||
id: 'uuid'
|
||||
});
|
||||
function update(m) {
|
||||
if (m.reload != null) {
|
||||
store.reload();
|
||||
return;
|
||||
}
|
||||
if (m.update == null)
|
||||
return;
|
||||
var r = store.getById(m.uuid);
|
||||
if (!r) {
|
||||
store.reload();
|
||||
return;
|
||||
}
|
||||
r.data.subs = m.subs;
|
||||
r.data.weight = m.weight;
|
||||
r.data.signal = m.signal;
|
||||
r.data.ber = m.ber;
|
||||
r.data.unc = m.unc;
|
||||
r.data.snr = m.snr;
|
||||
r.data.bps = m.bps;
|
||||
r.data.cc = m.cc;
|
||||
r.data.te = m.te;
|
||||
r.data.signal_scale = m.signal_scale;
|
||||
r.data.snr_scale = m.snr_scale;
|
||||
r.data.ec_bit = m.ec_bit;
|
||||
r.data.tc_bit = m.tc_bit;
|
||||
r.data.ec_block = m.ec_block;
|
||||
r.data.tc_block = m.tc_block;
|
||||
|
||||
function renderBw(value, item, store) {
|
||||
var txt = parseInt(value / 1024);
|
||||
var href = "javascript:tvheadend.stream_bw_monitor('" + store.id + "');";
|
||||
return '<a href="' + href + '">' + txt + '</a>';
|
||||
store.afterEdit(r);
|
||||
store.fireEvent('updated', store, Ext.data.Record.COMMIT);
|
||||
}
|
||||
|
||||
function renderBer(value, item, store) {
|
||||
if (store.data.tc_bit == 0)
|
||||
return value; // fallback (driver/vendor dependent ber)
|
||||
function builder() {
|
||||
if (grid)
|
||||
return;
|
||||
|
||||
// ber = error_bit_count / total_bit_count
|
||||
var ber = store.data.ec_bit / store.data.tc_bit;
|
||||
return ber;
|
||||
store = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
totalProperty: 'totalCount',
|
||||
fields: [
|
||||
{ name: 'uuid' },
|
||||
{ name: 'input' },
|
||||
{ name: 'username' },
|
||||
{ name: 'stream' },
|
||||
{ name: 'subs' },
|
||||
{ name: 'weight' },
|
||||
{ name: 'signal' },
|
||||
{ name: 'ber' },
|
||||
{ name: 'unc' },
|
||||
{ name: 'snr' },
|
||||
{ name: 'bps' },
|
||||
{ name: 'cc' },
|
||||
{ name: 'te' },
|
||||
{ name: 'signal_scale' },
|
||||
{ name: 'snr_scale' },
|
||||
{ name: 'ec_bit' },
|
||||
{ name: 'tc_bit' },
|
||||
{ name: 'ec_block' },
|
||||
{ name: 'tc_block' }
|
||||
],
|
||||
url: 'api/status/inputs',
|
||||
autoLoad: true,
|
||||
id: 'uuid'
|
||||
});
|
||||
tvheadend.streamStatusStore = store;
|
||||
|
||||
tvheadend.comet.on('input_status', update);
|
||||
|
||||
function renderBw(value, item, record) {
|
||||
var txt = parseInt(value / 1024);
|
||||
var href = "javascript:tvheadend.stream_bw_monitor('" + record.id + "');";
|
||||
return '<a href="' + href + '">' + txt + '</a>';
|
||||
}
|
||||
|
||||
function renderBer(value, item, store) {
|
||||
if (store.data.tc_bit == 0)
|
||||
return value; // fallback (driver/vendor dependent ber)
|
||||
|
||||
// ber = error_bit_count / total_bit_count
|
||||
var ber = store.data.ec_bit / store.data.tc_bit;
|
||||
return ber;
|
||||
}
|
||||
|
||||
function renderPer(value, item, store) {
|
||||
if (value == 0) // value: total_block_count
|
||||
return '<span class="tvh-grid-unset">Unknown</span>';
|
||||
|
||||
// per = error_block_count / total_block_count
|
||||
var per = store.data.ec_block / value;
|
||||
return per;
|
||||
}
|
||||
|
||||
var cm = new Ext.grid.ColumnModel([
|
||||
{
|
||||
width: 120,
|
||||
header: "Input",
|
||||
dataIndex: 'input'
|
||||
},
|
||||
{
|
||||
width: 100,
|
||||
header: "Stream",
|
||||
dataIndex: 'stream'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
header: "Subs #",
|
||||
dataIndex: 'subs'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
header: "Weight",
|
||||
dataIndex: 'weight'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
header: "Bandwidth (kb/s)",
|
||||
dataIndex: 'bps',
|
||||
renderer: renderBw
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
header: "BER",
|
||||
dataIndex: 'ber',
|
||||
renderer: renderBer
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
header: "PER",
|
||||
dataIndex: 'tc_block',
|
||||
renderer: renderPer
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
header: "Uncorrected Blocks",
|
||||
dataIndex: 'unc'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
header: "Transport Errors",
|
||||
dataIndex: 'te'
|
||||
},
|
||||
{
|
||||
width: 50,
|
||||
header: "Continuity Errors",
|
||||
dataIndex: 'cc'
|
||||
}
|
||||
]);
|
||||
|
||||
cm.config.push(new Ext.ux.grid.ProgressColumn({
|
||||
header: "SNR",
|
||||
dataIndex: 'snr',
|
||||
width: 85,
|
||||
colored: true,
|
||||
ceiling: 65535,
|
||||
tvh_renderer: function(v, p, record) {
|
||||
var scale = record.get('snr_scale');
|
||||
if (scale == 1)
|
||||
return v;
|
||||
if (scale == 2 && v > 0) {
|
||||
var snr = v * 0.0001;
|
||||
return snr.toFixed(1) + " dB";
|
||||
}
|
||||
return '<span class="tvh-grid-unset">Unknown</span>';
|
||||
},
|
||||
destroy: function() {
|
||||
}
|
||||
}));
|
||||
|
||||
cm.config.push(new Ext.ux.grid.ProgressColumn({
|
||||
header: "Signal Strength",
|
||||
dataIndex: 'signal',
|
||||
width: 85,
|
||||
colored: true,
|
||||
ceiling: 65535,
|
||||
tvh_renderer: function(v, p, record) {
|
||||
var scale = record.get('snr_scale');
|
||||
if (scale == 1)
|
||||
return v;
|
||||
if (scale == 2 && v > 0) {
|
||||
var snr = v * 0.0001;
|
||||
return snr.toFixed(1) + " dBm";
|
||||
}
|
||||
return '<span class="tvh-grid-unset">Unknown</span>';
|
||||
},
|
||||
destroy: function() {
|
||||
}
|
||||
}));
|
||||
|
||||
grid = new Ext.grid.GridPanel({
|
||||
border: false,
|
||||
loadMask: true,
|
||||
stripeRows: true,
|
||||
disableSelection: true,
|
||||
store: store,
|
||||
cm: cm,
|
||||
flex: 1,
|
||||
viewConfig: {
|
||||
forceFit: true
|
||||
}
|
||||
});
|
||||
|
||||
dpanel.add(grid);
|
||||
dpanel.doLayout(false, true);
|
||||
}
|
||||
|
||||
function renderPer(value, item, store) {
|
||||
if (value == 0) // value: total_block_count
|
||||
return '<span class="tvh-grid-unset">Unknown</span>';
|
||||
|
||||
// per = error_block_count / total_block_count
|
||||
var per = store.data.ec_block / value;
|
||||
return per;
|
||||
function destroyer() {
|
||||
if (grid === null || !tvheadend.dynamic)
|
||||
return;
|
||||
dpanel.removeAll()
|
||||
tvheadend.streamStatusStore = null;
|
||||
store.destroy();
|
||||
store = null;
|
||||
grid = null;
|
||||
}
|
||||
|
||||
var cm = new Ext.grid.ColumnModel([{
|
||||
width: 120,
|
||||
header: "Input",
|
||||
dataIndex: 'input'
|
||||
}, {
|
||||
width: 100,
|
||||
header: "Stream",
|
||||
dataIndex: 'stream'
|
||||
}, {
|
||||
width: 50,
|
||||
header: "Subs #",
|
||||
dataIndex: 'subs'
|
||||
}, {
|
||||
width: 50,
|
||||
header: "Weight",
|
||||
dataIndex: 'weight'
|
||||
}, {
|
||||
width: 50,
|
||||
header: "Bandwidth (kb/s)",
|
||||
dataIndex: 'bps',
|
||||
renderer: renderBw
|
||||
}, {
|
||||
width: 50,
|
||||
header: "BER",
|
||||
dataIndex: 'ber',
|
||||
renderer: renderBer
|
||||
}, {
|
||||
width: 50,
|
||||
header: "PER",
|
||||
dataIndex: 'tc_block',
|
||||
renderer: renderPer
|
||||
}, {
|
||||
width: 50,
|
||||
header: "Uncorrected Blocks",
|
||||
dataIndex: 'unc'
|
||||
}, {
|
||||
width: 50,
|
||||
header: "Transport Errors",
|
||||
dataIndex: 'te'
|
||||
}, {
|
||||
width: 50,
|
||||
header: "Continuity Errors",
|
||||
dataIndex: 'cc'
|
||||
}]);
|
||||
|
||||
cm.config.push(new Ext.ux.grid.ProgressColumn({
|
||||
header: "SNR",
|
||||
dataIndex: 'snr',
|
||||
width: 85,
|
||||
colored: true,
|
||||
ceiling: 65535,
|
||||
tvh_renderer: function(v, p, record) {
|
||||
var scale = record.get('snr_scale');
|
||||
if (scale == 1)
|
||||
return v;
|
||||
if (scale == 2 && v > 0) {
|
||||
var snr = v * 0.0001;
|
||||
return snr.toFixed(1) + " dB";
|
||||
}
|
||||
return '<span class="tvh-grid-unset">Unknown</span>';
|
||||
}
|
||||
}));
|
||||
|
||||
cm.config.push(new Ext.ux.grid.ProgressColumn({
|
||||
header: "Signal Strength",
|
||||
dataIndex: 'signal',
|
||||
width: 85,
|
||||
colored: true,
|
||||
ceiling: 65535,
|
||||
tvh_renderer: function(v, p, record) {
|
||||
var scale = record.get('snr_scale');
|
||||
if (scale == 1)
|
||||
return v;
|
||||
if (scale == 2 && v > 0) {
|
||||
var snr = v * 0.0001;
|
||||
return snr.toFixed(1) + " dBm";
|
||||
}
|
||||
return '<span class="tvh-grid-unset">Unknown</span>';
|
||||
}
|
||||
}));
|
||||
|
||||
tvheadend.comet.on('input_status', function(m) {
|
||||
if (m.reload != null)
|
||||
tvheadend.streamStatusStore.reload();
|
||||
if (m.update != null) {
|
||||
var r = tvheadend.streamStatusStore.getById(m.uuid);
|
||||
if (r) {
|
||||
r.data.subs = m.subs;
|
||||
r.data.weight = m.weight;
|
||||
r.data.signal = m.signal;
|
||||
r.data.ber = m.ber;
|
||||
r.data.unc = m.unc;
|
||||
r.data.snr = m.snr;
|
||||
r.data.bps = m.bps;
|
||||
r.data.cc = m.cc;
|
||||
r.data.te = m.te;
|
||||
r.data.signal_scale = m.signal_scale;
|
||||
r.data.snr_scale = m.snr_scale;
|
||||
r.data.ec_bit = m.ec_bit;
|
||||
r.data.tc_bit = m.tc_bit;
|
||||
r.data.ec_block = m.ec_block;
|
||||
r.data.tc_block = m.tc_block;
|
||||
|
||||
tvheadend.streamStatusStore.afterEdit(r);
|
||||
tvheadend.streamStatusStore.fireEvent('updated',
|
||||
tvheadend.streamStatusStore,
|
||||
r,
|
||||
Ext.data.Record.COMMIT);
|
||||
} else {
|
||||
tvheadend.streamStatusStore.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var panel = new Ext.grid.GridPanel({
|
||||
var dpanel = new Ext.Panel({
|
||||
border: false,
|
||||
loadMask: true,
|
||||
stripeRows: true,
|
||||
disableSelection: true,
|
||||
header: false,
|
||||
layout: 'fit',
|
||||
title: 'Stream',
|
||||
iconCls: 'hardware',
|
||||
store: tvheadend.streamStatusStore,
|
||||
cm: cm,
|
||||
flex: 1,
|
||||
viewConfig: {
|
||||
forceFit: true
|
||||
}
|
||||
iconCls: 'hardware'
|
||||
});
|
||||
return panel;
|
||||
|
||||
tvheadend.paneladd(panel, dpanel, index);
|
||||
tvheadend.panelreg(panel, dpanel, builder, destroyer);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
tvheadend.status_conns = function() {
|
||||
tvheadend.status_conns = function(panel, index) {
|
||||
|
||||
var store = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
totalProperty: 'totalCount',
|
||||
fields: [{
|
||||
name: 'id'
|
||||
}, {
|
||||
name: 'type'
|
||||
}, {
|
||||
name: 'peer'
|
||||
}, {
|
||||
name: 'user'
|
||||
}, {
|
||||
name: 'started',
|
||||
type: 'date',
|
||||
dateFormat: 'U' /* unix time */
|
||||
}],
|
||||
url: 'api/status/connections',
|
||||
autoLoad: true,
|
||||
id: 'id'
|
||||
});
|
||||
var grid = null;
|
||||
var store = null;
|
||||
|
||||
tvheadend.comet.on('connections', function(m) {
|
||||
function update(m) {
|
||||
if (m.reload != null)
|
||||
store.reload();
|
||||
});
|
||||
|
||||
function renderDate(value) {
|
||||
var dt = new Date(value);
|
||||
return dt.format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
var cm = new Ext.grid.ColumnModel([{
|
||||
width: 50,
|
||||
id: 'type',
|
||||
header: "Type",
|
||||
dataIndex: 'type'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'peer',
|
||||
header: "IP Address",
|
||||
dataIndex: 'peer'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'user',
|
||||
header: "Username",
|
||||
dataIndex: 'user'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'started',
|
||||
header: "Started",
|
||||
dataIndex: 'started',
|
||||
renderer: renderDate
|
||||
}]);
|
||||
function builder() {
|
||||
if (grid)
|
||||
return;
|
||||
|
||||
var panel = new Ext.grid.GridPanel({
|
||||
border: false,
|
||||
loadMask: true,
|
||||
stripeRows: true,
|
||||
disableSelection: true,
|
||||
title: 'Connections',
|
||||
iconCls: 'eye',
|
||||
store: store,
|
||||
cm: cm,
|
||||
flex: 1,
|
||||
viewConfig: {
|
||||
forceFit: true
|
||||
store = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
totalProperty: 'totalCount',
|
||||
fields: [
|
||||
{ name: 'id' },
|
||||
{ name: 'type' },
|
||||
{ name: 'peer' },
|
||||
{ name: 'user' },
|
||||
{
|
||||
name: 'started',
|
||||
type: 'date',
|
||||
dateFormat: 'U' /* unix time */
|
||||
}
|
||||
],
|
||||
url: 'api/status/connections',
|
||||
autoLoad: true,
|
||||
id: 'id'
|
||||
});
|
||||
|
||||
tvheadend.comet.on('connections', update);
|
||||
|
||||
function renderDate(value) {
|
||||
var dt = new Date(value);
|
||||
return dt.format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
var cm = new Ext.grid.ColumnModel([{
|
||||
width: 50,
|
||||
id: 'type',
|
||||
header: "Type",
|
||||
dataIndex: 'type'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'peer',
|
||||
header: "IP Address",
|
||||
dataIndex: 'peer'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'user',
|
||||
header: "Username",
|
||||
dataIndex: 'user'
|
||||
}, {
|
||||
width: 50,
|
||||
id: 'started',
|
||||
header: "Started",
|
||||
dataIndex: 'started',
|
||||
renderer: renderDate
|
||||
}]);
|
||||
|
||||
grid = new Ext.grid.GridPanel({
|
||||
border: false,
|
||||
loadMask: true,
|
||||
stripeRows: true,
|
||||
disableSelection: true,
|
||||
store: store,
|
||||
cm: cm,
|
||||
flex: 1,
|
||||
viewConfig: {
|
||||
forceFit: true
|
||||
}
|
||||
});
|
||||
|
||||
dpanel.add(grid);
|
||||
dpanel.doLayout(false, true);
|
||||
}
|
||||
|
||||
function destroyer() {
|
||||
if (grid === null || !tvheadend.dynamic)
|
||||
return;
|
||||
dpanel.removeAll()
|
||||
store.destroy();
|
||||
store = null;
|
||||
grid = null;
|
||||
}
|
||||
|
||||
var dpanel = new Ext.Panel({
|
||||
border: false,
|
||||
header: false,
|
||||
layout: 'fit',
|
||||
title: 'Connections',
|
||||
iconCls: 'eye'
|
||||
});
|
||||
return panel;
|
||||
|
||||
tvheadend.paneladd(panel, dpanel, index);
|
||||
tvheadend.panelreg(panel, dpanel, builder, destroyer);
|
||||
};
|
||||
|
||||
tvheadend.status = function() {
|
||||
|
@ -437,13 +521,12 @@ tvheadend.status = function() {
|
|||
autoScroll: true,
|
||||
activeTab: 0,
|
||||
iconCls: 'eye',
|
||||
items: [
|
||||
new tvheadend.status_streams,
|
||||
new tvheadend.status_subs,
|
||||
new tvheadend.status_conns,
|
||||
new tvheadend.service_mapper_status
|
||||
]
|
||||
items: [],
|
||||
});
|
||||
tvheadend.status_streams(panel);
|
||||
tvheadend.status_subs(panel);
|
||||
tvheadend.status_conns(panel);
|
||||
tvheadend.service_mapper_status(panel);
|
||||
return panel;
|
||||
};
|
||||
|
||||
|
@ -517,8 +600,9 @@ tvheadend.subscription_bw_monitor = function(id) {
|
|||
var task = {
|
||||
interval: 1000,
|
||||
run: function() {
|
||||
r = tvheadend.subsStore.getById(id);
|
||||
if (typeof r === 'undefined') {
|
||||
var store = tvheadend.subsStore;
|
||||
var r = store ? store.getById(id) : null;
|
||||
if (!store || typeof r === 'undefined') {
|
||||
chart.stop();
|
||||
Ext.TaskMgr.stop(task);
|
||||
return;
|
||||
|
@ -607,10 +691,11 @@ tvheadend.stream_bw_monitor = function(id) {
|
|||
});
|
||||
|
||||
var task = {
|
||||
interval: 1000,
|
||||
interval: 10000,
|
||||
run: function() {
|
||||
r = tvheadend.streamStatusStore.getById(id);
|
||||
if (typeof r === 'undefined') {
|
||||
var store = tvheadend.streamStatusStore;
|
||||
var r = store ? store.getById(id) : null;
|
||||
if (!store || typeof r === 'undefined') {
|
||||
chart.stop();
|
||||
Ext.TaskMgr.stop(task);
|
||||
return;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
tvheadend.timeshift = function() {
|
||||
tvheadend.timeshift = function(panel, index) {
|
||||
|
||||
/* ****************************************************************
|
||||
* Data
|
||||
|
@ -177,5 +177,5 @@ tvheadend.timeshift = function() {
|
|||
});
|
||||
}
|
||||
|
||||
return confpanel;
|
||||
tvheadend.paneladd(panel, confpanel, index);
|
||||
};
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
tvheadend.tvadapters = function() {
|
||||
return tvheadend.idnode_tree({
|
||||
tvheadend.tvadapters = function(panel, index) {
|
||||
|
||||
tvheadend.idnode_tree(panel, {
|
||||
url: 'api/hardware/tree',
|
||||
title: 'TV adapters',
|
||||
comet: 'hardware',
|
||||
tabIndex: index,
|
||||
help: function() {
|
||||
new tvheadend.help('TV adapters', 'config_tvadapters.html');
|
||||
}
|
||||
});
|
||||
|
||||
return panel;
|
||||
};
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
tvheadend.dynamic = true;
|
||||
tvheadend.accessupdate = null;
|
||||
tvheadend.capabilties = null;
|
||||
tvheadend.conf_chepg = null;
|
||||
tvheadend.conf_dvbin = null;
|
||||
tvheadend.conf_tsdvr = null;
|
||||
tvheadend.conf_csa = null;
|
||||
tvheadend.dvrpanel = null;
|
||||
tvheadend.confpanel = null;
|
||||
|
||||
/* State Provider */
|
||||
Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
|
||||
|
@ -40,6 +39,100 @@ tvheadend.help = function(title, pagename) {
|
|||
});
|
||||
};
|
||||
|
||||
tvheadend.paneladd = function(dst, add, idx) {
|
||||
if (idx != null)
|
||||
dst.insert(idx, add);
|
||||
else
|
||||
dst.add(add);
|
||||
};
|
||||
|
||||
tvheadend.panelreg = function(tabpanel, panel, builder, destroyer) {
|
||||
/* the 'activate' event does not work in ExtJS 3.4 */
|
||||
tabpanel.on('beforetabchange', function(tp, p) {
|
||||
if (p == panel)
|
||||
builder();
|
||||
});
|
||||
panel.on('deactivate', destroyer);
|
||||
}
|
||||
|
||||
tvheadend.Ajax = function(conf) {
|
||||
var orig_success = conf.success;
|
||||
var orig_failure = conf.failure;
|
||||
conf.success = function(d) {
|
||||
tvheadend.loading(0);
|
||||
if (orig_success)
|
||||
orig_success(d);
|
||||
}
|
||||
conf.failure = function(d) {
|
||||
tvheadend.loading(0);
|
||||
if (orig_failure)
|
||||
orig_failure(d);
|
||||
}
|
||||
tvheadend.loading(1);
|
||||
Ext.Ajax.request(conf);
|
||||
};
|
||||
|
||||
tvheadend.AjaxConfirm = function(conf) {
|
||||
Ext.MessageBox.confirm(
|
||||
conf.title || 'Message',
|
||||
conf.question || 'Do you really want to delete the selection?',
|
||||
function (btn) {
|
||||
if (btn == 'yes')
|
||||
tvheadend.Ajax(conf);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
tvheadend.loading = function(on) {
|
||||
if (on)
|
||||
Ext.getBody().mask('Loading... Please, wait...', 'loading');
|
||||
else
|
||||
Ext.getBody().unmask();
|
||||
};
|
||||
|
||||
/*
|
||||
* Any Match option in ComboBox queries
|
||||
* This query is identical as in extjs-all.js
|
||||
* except one
|
||||
*/
|
||||
tvheadend.doQueryAnyMatch = function(q, forceAll) {
|
||||
q = Ext.isEmpty(q) ? '' : q;
|
||||
var qe = {
|
||||
query: q,
|
||||
forceAll: forceAll,
|
||||
combo: this,
|
||||
cancel:false
|
||||
};
|
||||
|
||||
if (this.fireEvent('beforequery', qe) === false || qe.cancel)
|
||||
return false;
|
||||
|
||||
q = qe.query;
|
||||
forceAll = qe.forceAll;
|
||||
if (forceAll === true || (q.length >= this.minChars)) {
|
||||
if (this.lastQuery !== q) {
|
||||
this.lastQuery = q;
|
||||
if (this.mode == 'local') {
|
||||
this.selectedIndex = -1;
|
||||
if (forceAll) {
|
||||
this.store.clearFilter();
|
||||
} else {
|
||||
/* supply the anyMatch option (last param) */
|
||||
this.store.filter(this.displayField, q, true);
|
||||
}
|
||||
this.onLoad();
|
||||
} else {
|
||||
this.store.baseParams[this.queryParam] = q;
|
||||
this.store.load({ params: this.getParams(q) });
|
||||
this.expand();
|
||||
}
|
||||
} else {
|
||||
this.selectedIndex = -1;
|
||||
this.onLoad();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* General capabilities
|
||||
*/
|
||||
|
@ -218,109 +311,119 @@ function accessUpdate(o) {
|
|||
if (!tvheadend.capabilities)
|
||||
return;
|
||||
|
||||
tvheadend.rootTabPanel.setLogin(o.username);
|
||||
|
||||
if (tvheadend.autorecButton)
|
||||
tvheadend.autorecButton.setDisabled(o.dvr != true);
|
||||
|
||||
if (o.dvr == true && tvheadend.dvrpanel == null) {
|
||||
tvheadend.dvrpanel = new tvheadend.dvr;
|
||||
tvheadend.dvrpanel = tvheadend.dvr();
|
||||
tvheadend.rootTabPanel.add(tvheadend.dvrpanel);
|
||||
}
|
||||
|
||||
if (o.admin == true && tvheadend.confpanel == null) {
|
||||
var tabs1 = [
|
||||
new tvheadend.miscconf,
|
||||
new tvheadend.acleditor
|
||||
];
|
||||
var tabs2;
|
||||
|
||||
/* DVB inputs */
|
||||
tabs2 = [];
|
||||
if (tvheadend.capabilities.indexOf('linuxdvb') !== -1 ||
|
||||
tvheadend.capabilities.indexOf('satip_client') !== -1 ||
|
||||
tvheadend.capabilities.indexOf('v4l') !== -1) {
|
||||
tabs2.push(new tvheadend.tvadapters);
|
||||
}
|
||||
/*
|
||||
tabs2.push(new tvheadend.iptv);
|
||||
*/
|
||||
tvheadend.conf_dvbin = new Ext.TabPanel({
|
||||
var cp = new Ext.TabPanel({
|
||||
activeTab: 0,
|
||||
autoScroll: true,
|
||||
title: 'Configuration',
|
||||
iconCls: 'wrench',
|
||||
items: []
|
||||
});
|
||||
|
||||
tvheadend.miscconf(cp);
|
||||
|
||||
tvheadend.acleditor(cp);
|
||||
|
||||
/* DVB inputs, networks, muxes, services */
|
||||
var dvbin = new Ext.TabPanel({
|
||||
activeTab: 0,
|
||||
autoScroll: true,
|
||||
title: 'DVB Inputs',
|
||||
iconCls: 'hardware',
|
||||
items: tabs2
|
||||
items: []
|
||||
});
|
||||
tvheadend.networks(tvheadend.conf_dvbin);
|
||||
tvheadend.muxes(tvheadend.conf_dvbin);
|
||||
tvheadend.services(tvheadend.conf_dvbin);
|
||||
tvheadend.mux_sched(tvheadend.conf_dvbin);
|
||||
tabs1.push(tvheadend.conf_dvbin);
|
||||
|
||||
var idx = 0;
|
||||
|
||||
if (tvheadend.capabilities.indexOf('linuxdvb') !== -1 ||
|
||||
tvheadend.capabilities.indexOf('satip_client') !== -1 ||
|
||||
tvheadend.capabilities.indexOf('v4l') !== -1)
|
||||
tvheadend.tvadapters(dvbin);
|
||||
tvheadend.networks(dvbin);
|
||||
tvheadend.muxes(dvbin);
|
||||
tvheadend.services(dvbin);
|
||||
tvheadend.mux_sched(dvbin);
|
||||
|
||||
cp.add(dvbin);
|
||||
|
||||
/* Channel / EPG */
|
||||
tvheadend.conf_chepg = new Ext.TabPanel({
|
||||
var chepg = new Ext.TabPanel({
|
||||
activeTab: 0,
|
||||
autoScroll: true,
|
||||
title: 'Channel / EPG',
|
||||
iconCls: 'television',
|
||||
items: []
|
||||
});
|
||||
tvheadend.channel_tab(tvheadend.conf_chepg, 0);
|
||||
tvheadend.cteditor(tvheadend.conf_chepg, 1);
|
||||
tvheadend.conf_chepg.insert(2, new tvheadend.epggrab);
|
||||
tabs1.push(tvheadend.conf_chepg);
|
||||
tvheadend.channel_tab(chepg);
|
||||
tvheadend.cteditor(chepg);
|
||||
tvheadend.epggrab(chepg);
|
||||
|
||||
cp.add(chepg);
|
||||
|
||||
/* DVR / Timeshift */
|
||||
tabs2 = [new tvheadend.dvrsettings];
|
||||
if (tvheadend.capabilities.indexOf('timeshift') !== -1) {
|
||||
tabs2.push(new tvheadend.timeshift);
|
||||
}
|
||||
tvheadend.conf_tsdvr = new Ext.TabPanel({
|
||||
var tsdvr = new Ext.TabPanel({
|
||||
activeTab: 0,
|
||||
autoScroll: true,
|
||||
title: 'Recording',
|
||||
iconCls: 'drive',
|
||||
items: tabs2
|
||||
items: []
|
||||
});
|
||||
tabs1.push(tvheadend.conf_tsdvr);
|
||||
tvheadend.dvr_settings(tsdvr);
|
||||
if (tvheadend.capabilities.indexOf('timeshift') !== -1)
|
||||
tvheadend.timeshift(tsdvr);
|
||||
|
||||
cp.add(tsdvr);
|
||||
|
||||
/* CSA */
|
||||
tabs2 = [];
|
||||
if (tvheadend.capabilities.indexOf('cwc') !== -1)
|
||||
tabs2.push(new tvheadend.cwceditor);
|
||||
if (tvheadend.capabilities.indexOf('capmt') !== -1)
|
||||
tabs2.push(new tvheadend.capmteditor);
|
||||
if (tabs2.length > 0) {
|
||||
tvheadend.conf_csa = new Ext.TabPanel({
|
||||
if (tvheadend.capabilities.indexOf('cwc') !== -1 ||
|
||||
tvheadend.capabilities.indexOf('capmt') !== -1) {
|
||||
|
||||
var csa = new Ext.TabPanel({
|
||||
activeTab: 0,
|
||||
autoScroll: true,
|
||||
title: 'CSA',
|
||||
iconCls: 'key',
|
||||
items: tabs2
|
||||
items: []
|
||||
});
|
||||
tabs1.push(tvheadend.conf_csa);
|
||||
|
||||
if (tvheadend.capabilities.indexOf('cwc') !== -1)
|
||||
tvheadend.cwceditor(csa);
|
||||
if (tvheadend.capabilities.indexOf('capmt') !== -1)
|
||||
tvheadend.capmteditor(csa);
|
||||
|
||||
cp.add(csa);
|
||||
}
|
||||
|
||||
/* Stream Config */
|
||||
tvheadend.conf_stream = new Ext.TabPanel({
|
||||
var stream = new Ext.TabPanel({
|
||||
activeTab: 0,
|
||||
autoScroll: true,
|
||||
title: 'Stream',
|
||||
iconCls: 'stream_config',
|
||||
items: []
|
||||
});
|
||||
tvheadend.esfilter_tab(tvheadend.conf_stream);
|
||||
tabs1.push(tvheadend.conf_stream);
|
||||
tvheadend.esfilter_tab(stream);
|
||||
|
||||
cp.add(stream);
|
||||
|
||||
/* Debug */
|
||||
tabs1.push(new tvheadend.tvhlog);
|
||||
tvheadend.tvhlog(cp);
|
||||
|
||||
tvheadend.confpanel = new Ext.TabPanel({
|
||||
activeTab: 0,
|
||||
autoScroll: true,
|
||||
title: 'Configuration',
|
||||
iconCls: 'wrench',
|
||||
items: tabs1
|
||||
});
|
||||
|
||||
tvheadend.rootTabPanel.add(tvheadend.confpanel);
|
||||
tvheadend.confpanel.doLayout();
|
||||
/* Finish */
|
||||
tvheadend.rootTabPanel.add(cp);
|
||||
tvheadend.confpanel = cp;
|
||||
cp.doLayout();
|
||||
}
|
||||
|
||||
if (o.admin == true && tvheadend.statuspanel == null) {
|
||||
|
@ -365,6 +468,86 @@ tvheadend.log = function(msg, style) {
|
|||
e.scrollIntoView('systemlog');
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
tvheadend.RootTabPanel = Ext.extend(Ext.TabPanel, {
|
||||
|
||||
onRender: function(ct, position) {
|
||||
tvheadend.RootTabPanel.superclass.onRender.call(this, ct, position);
|
||||
|
||||
/* Create login components */
|
||||
var before = this.strip.dom.childNodes[this.strip.dom.childNodes.length-1];
|
||||
|
||||
if (!this.loginTpl) {
|
||||
var tt = new Ext.Template(
|
||||
'<li class="x-tab-login" id="{id}">',
|
||||
'<span class="x-tab-strip-login {iconCls}">{text}</span></li>'
|
||||
);
|
||||
tt.disableFormats = true;
|
||||
tt.compile();
|
||||
tvheadend.RootTabPanel.prototype.loginTpl = tt;
|
||||
}
|
||||
var item = new Ext.Component();
|
||||
var p = this.getTemplateArgs(item);
|
||||
var before = this.strip.dom.childNodes[this.strip.dom.childNodes.length-1];
|
||||
item.tabEl = this.loginTpl.insertBefore(before, p);
|
||||
this.loginItem = item;
|
||||
|
||||
if (!this.loginCmdTpl) {
|
||||
var tt = new Ext.Template(
|
||||
'<li class="x-tab-login" id="{id}"><a href="#">',
|
||||
'<span class="x-tab-strip-login-cmd"></span></a></li>'
|
||||
);
|
||||
tt.disableFormats = true;
|
||||
tt.compile();
|
||||
tvheadend.RootTabPanel.prototype.loginCmdTpl = tt;
|
||||
}
|
||||
var item = new Ext.Component();
|
||||
var p = this.getTemplateArgs(item);
|
||||
var el = this.loginCmdTpl.insertBefore(before, p);
|
||||
item.tabEl = Ext.get(el);
|
||||
item.tabEl.select('a').on('click', this.onLoginCmdClicked, this, {preventDefault: true});
|
||||
this.loginCmdItem = item;
|
||||
|
||||
this.on('beforetabchange', function(tp, p) {
|
||||
if (p == this.loginItem || p == this.loginCmdItem)
|
||||
return false;
|
||||
});
|
||||
|
||||
this.setLogin('');
|
||||
},
|
||||
|
||||
getComponent: function(comp) {
|
||||
if (comp === this.loginItem.id || comp == this.loginItem)
|
||||
return this.loginItem;
|
||||
if (comp === this.loginCmdItem.id || comp == this.loginCmdItem)
|
||||
return this.loginCmdItem;
|
||||
return tvheadend.RootTabPanel.superclass.getComponent.call(this, comp);
|
||||
},
|
||||
|
||||
setLogin: function(login) {
|
||||
this.login = login;
|
||||
if (login) {
|
||||
text = 'Logged in as <b>' + login + '</b>';
|
||||
cmd = '(logout)';
|
||||
} else {
|
||||
text = 'No verified access';
|
||||
cmd = '(login)';
|
||||
}
|
||||
var el = this.loginItem.tabEl;
|
||||
var fly = Ext.fly(this.loginItem.tabEl);
|
||||
var t = fly.child('span.x-tab-strip-login', true);
|
||||
Ext.fly(this.loginItem.tabEl).child('span.x-tab-strip-login', true).innerHTML = text;
|
||||
Ext.fly(this.loginCmdItem.tabEl).child('span.x-tab-strip-login-cmd', true).innerHTML = cmd;
|
||||
},
|
||||
|
||||
onLoginCmdClicked: function(e) {
|
||||
window.location.href = this.login ? 'logout' : 'login';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -386,10 +569,10 @@ tvheadend.app = function() {
|
|||
html: '<div id="header"><h1>Tvheadend Web-Panel</h1></div>'
|
||||
});
|
||||
|
||||
tvheadend.rootTabPanel = new Ext.TabPanel({
|
||||
tvheadend.rootTabPanel = new tvheadend.RootTabPanel({
|
||||
region: 'center',
|
||||
activeTab: 0,
|
||||
items: [new tvheadend.epg]
|
||||
items: [tvheadend.epg()]
|
||||
});
|
||||
|
||||
var viewport = new Ext.Viewport({
|
||||
|
@ -439,4 +622,3 @@ tvheadend.app = function() {
|
|||
|
||||
};
|
||||
}(); // end of app
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
tvheadend.tvhlog = function() {
|
||||
tvheadend.tvhlog = function(panel, index) {
|
||||
/*
|
||||
* Basic Config
|
||||
*/
|
||||
|
@ -115,5 +115,5 @@ tvheadend.tvhlog = function() {
|
|||
});
|
||||
}
|
||||
|
||||
return confpanel;
|
||||
tvheadend.paneladd(panel, confpanel, index);
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue