DVR: Time Based Recorder
This commit is contained in:
parent
d7b7f72845
commit
6de4fb94ea
11 changed files with 937 additions and 92 deletions
1
Makefile
1
Makefile
|
@ -178,6 +178,7 @@ SRCS += src/plumbing/tsfix.c \
|
|||
SRCS += src/dvr/dvr_db.c \
|
||||
src/dvr/dvr_rec.c \
|
||||
src/dvr/dvr_autorec.c \
|
||||
src/dvr/dvr_timerec.c \
|
||||
src/dvr/dvr_cutpoints.c \
|
||||
|
||||
SRCS += src/webui/webui.c \
|
||||
|
|
10
docs/html/config_dvrtime.html
Normal file
10
docs/html/config_dvrtime.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 time-based automatic recording.
|
||||
|
||||
<p>
|
||||
A volunteer required to fill this...
|
||||
|
||||
</div>
|
|
@ -330,6 +330,40 @@ api_dvr_autorec_create_by_series
|
|||
return !count ? EINVAL : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
api_dvr_timerec_grid
|
||||
( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
|
||||
{
|
||||
dvr_timerec_entry_t *dte;
|
||||
|
||||
TAILQ_FOREACH(dte, &timerec_entries, dte_link)
|
||||
idnode_set_add(ins, (idnode_t*)dte, &conf->filter);
|
||||
}
|
||||
|
||||
static int
|
||||
api_dvr_timerec_create
|
||||
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
htsmsg_t *conf;
|
||||
dvr_timerec_entry_t *dte;
|
||||
|
||||
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);
|
||||
dte = dvr_timerec_create(NULL, conf);
|
||||
if (dte) {
|
||||
dvr_timerec_save(dte);
|
||||
dvr_timerec_check(dte);
|
||||
}
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void api_dvr_init ( void )
|
||||
{
|
||||
static api_hook_t ah[] = {
|
||||
|
@ -351,6 +385,10 @@ void api_dvr_init ( void )
|
|||
{ "dvr/autorec/create", ACCESS_RECORDER, api_dvr_autorec_create, NULL },
|
||||
{ "dvr/autorec/create_by_series", ACCESS_RECORDER, api_dvr_autorec_create_by_series, NULL },
|
||||
|
||||
{ "dvr/timerec/class", ACCESS_RECORDER, api_idnode_class, (void*)&dvr_timerec_entry_class },
|
||||
{ "dvr/timerec/grid", ACCESS_RECORDER, api_idnode_grid, api_dvr_timerec_grid },
|
||||
{ "dvr/timerec/create", ACCESS_RECORDER, api_dvr_timerec_create, NULL },
|
||||
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
|
|
|
@ -197,6 +197,17 @@ channel_class_get_title ( idnode_t *self )
|
|||
return channel_get_name((channel_t*)self);
|
||||
}
|
||||
|
||||
/* exported for others */
|
||||
htsmsg_t *
|
||||
channel_class_get_list(void *o)
|
||||
{
|
||||
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");
|
||||
return m;
|
||||
}
|
||||
|
||||
static const void *
|
||||
channel_class_get_name ( void *p )
|
||||
{
|
||||
|
@ -531,6 +542,12 @@ channel_create0
|
|||
{
|
||||
lock_assert(&global_lock);
|
||||
|
||||
LIST_INIT(&ch->ch_services);
|
||||
LIST_INIT(&ch->ch_subscriptions);
|
||||
LIST_INIT(&ch->ch_epggrab);
|
||||
LIST_INIT(&ch->ch_autorecs);
|
||||
LIST_INIT(&ch->ch_timerecs);
|
||||
|
||||
if (idnode_insert(&ch->ch_id, uuid, idc, IDNODE_SHORT_UUID)) {
|
||||
if (uuid)
|
||||
tvherror("channel", "invalid uuid '%s'", uuid);
|
||||
|
@ -578,6 +595,7 @@ channel_delete ( channel_t *ch, int delconf )
|
|||
|
||||
/* DVR */
|
||||
autorec_destroy_by_channel(ch, delconf);
|
||||
timerec_destroy_by_channel(ch, delconf);
|
||||
dvr_destroy_by_channel(ch, delconf);
|
||||
|
||||
/* Services */
|
||||
|
|
|
@ -71,6 +71,7 @@ typedef struct channel
|
|||
int ch_dvr_extra_time_post;
|
||||
struct dvr_entry_list ch_dvrs;
|
||||
struct dvr_autorec_entry_list ch_autorecs;
|
||||
struct dvr_timerec_entry_list ch_timerecs;
|
||||
|
||||
} channel_t;
|
||||
|
||||
|
@ -152,6 +153,8 @@ channel_t *channel_find_by_number(int no);
|
|||
|
||||
#define channel_find channel_find_by_uuid
|
||||
|
||||
htsmsg_t * channel_class_get_list(void *o);
|
||||
|
||||
int channel_set_tags_by_list ( channel_t *ch, htsmsg_t *tags );
|
||||
int channel_set_services_by_list ( channel_t *ch, htsmsg_t *svcs );
|
||||
|
||||
|
|
|
@ -194,6 +194,11 @@ typedef struct dvr_entry {
|
|||
LIST_ENTRY(dvr_entry) de_autorec_link;
|
||||
struct dvr_autorec_entry *de_autorec;
|
||||
|
||||
/**
|
||||
* Timerec linkage
|
||||
*/
|
||||
struct dvr_timerec_entry *de_timerec;
|
||||
|
||||
/**
|
||||
* Fields for recording
|
||||
*/
|
||||
|
@ -251,7 +256,7 @@ typedef struct dvr_autorec_entry {
|
|||
channel_tag_t *dae_channel_tag;
|
||||
LIST_ENTRY(dvr_autorec_entry) dae_channel_tag_link;
|
||||
|
||||
dvr_prio_t dae_pri;
|
||||
int dae_pri;
|
||||
|
||||
struct dvr_entry_list dae_spawns;
|
||||
|
||||
|
@ -272,6 +277,42 @@ TAILQ_HEAD(dvr_autorec_entry_queue, dvr_autorec_entry);
|
|||
|
||||
extern struct dvr_autorec_entry_queue autorec_entries;
|
||||
|
||||
/**
|
||||
* Timerec entry
|
||||
*/
|
||||
typedef struct dvr_timerec_entry {
|
||||
idnode_t dte_id;
|
||||
|
||||
TAILQ_ENTRY(dvr_timerec_entry) dte_link;
|
||||
|
||||
char *dte_name;
|
||||
char *dte_config_name;
|
||||
|
||||
int dte_enabled;
|
||||
char *dte_creator;
|
||||
char *dte_comment;
|
||||
|
||||
char *dte_title;
|
||||
|
||||
int dte_start; /* Minutes from midnight */
|
||||
int dte_stop; /* Minutes from midnight */
|
||||
|
||||
uint32_t dte_weekdays;
|
||||
|
||||
channel_t *dte_channel;
|
||||
LIST_ENTRY(dvr_timerec_entry) dte_channel_link;
|
||||
|
||||
int dte_pri;
|
||||
|
||||
dvr_entry_t *dte_spawn;
|
||||
|
||||
int dte_retention;
|
||||
} dvr_timerec_entry_t;
|
||||
|
||||
TAILQ_HEAD(dvr_timerec_entry_queue, dvr_timerec_entry);
|
||||
|
||||
extern struct dvr_timerec_entry_queue timerec_entries;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -279,6 +320,7 @@ 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;
|
||||
extern const idclass_t dvr_timerec_entry_class;
|
||||
|
||||
/**
|
||||
* Prototypes
|
||||
|
@ -366,12 +408,6 @@ void dvr_config_init(void);
|
|||
|
||||
void dvr_done(void);
|
||||
|
||||
void dvr_autorec_init(void);
|
||||
|
||||
void dvr_autorec_done(void);
|
||||
|
||||
void dvr_autorec_update(void);
|
||||
|
||||
void dvr_destroy_by_channel(channel_t *ch, int delconf);
|
||||
|
||||
void dvr_rec_subscribe(dvr_entry_t *de);
|
||||
|
@ -436,6 +472,16 @@ int dvr_sort_start_ascending(const void *A, const void *B);
|
|||
dvr_autorec_entry_t *
|
||||
dvr_autorec_create(const char *uuid, htsmsg_t *conf);
|
||||
|
||||
dvr_entry_t *
|
||||
dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
|
||||
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_timerec_entry_t *tae,
|
||||
dvr_prio_t pri, int retention);
|
||||
|
||||
dvr_autorec_entry_t*
|
||||
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
|
||||
channel_t *ch, uint32_t aroundTime, uint32_t days,
|
||||
|
@ -458,18 +504,53 @@ dvr_autorec_find_by_uuid(const char *uuid)
|
|||
{ return (dvr_autorec_entry_t*)idnode_find(uuid, &dvr_autorec_entry_class); }
|
||||
|
||||
|
||||
htsmsg_t * dvr_autorec_entry_class_time_list(void *o, const char *null);
|
||||
htsmsg_t * dvr_autorec_entry_class_weekdays_list ( void *o );
|
||||
char * dvr_autorec_entry_class_weekdays_rend(uint32_t weekdays);
|
||||
|
||||
void dvr_autorec_check_event(epg_broadcast_t *e);
|
||||
void dvr_autorec_check_brand(epg_brand_t *b);
|
||||
void dvr_autorec_check_season(epg_season_t *s);
|
||||
void dvr_autorec_check_serieslink(epg_serieslink_t *s);
|
||||
|
||||
|
||||
void autorec_destroy_by_channel(channel_t *ch, int delconf);
|
||||
|
||||
void autorec_destroy_by_channel_tag(channel_tag_t *ct, int delconf);
|
||||
|
||||
void autorec_destroy_by_id(const char *id, int delconf);
|
||||
|
||||
void dvr_autorec_init(void);
|
||||
|
||||
void dvr_autorec_done(void);
|
||||
|
||||
void dvr_autorec_update(void);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
dvr_timerec_entry_t *
|
||||
dvr_timerec_create(const char *uuid, htsmsg_t *conf);
|
||||
|
||||
static inline dvr_timerec_entry_t *
|
||||
dvr_timerec_find_by_uuid(const char *uuid)
|
||||
{ return (dvr_timerec_entry_t*)idnode_find(uuid, &dvr_timerec_entry_class); }
|
||||
|
||||
|
||||
void dvr_timerec_save(dvr_timerec_entry_t *dae);
|
||||
|
||||
void dvr_timerec_check(dvr_timerec_entry_t *dae);
|
||||
|
||||
void timerec_destroy_by_channel(channel_t *ch, int delconf);
|
||||
|
||||
void timerec_destroy_by_id(const char *id, int delconf);
|
||||
|
||||
void dvr_timerec_init(void);
|
||||
|
||||
void dvr_timerec_done(void);
|
||||
|
||||
void dvr_timerec_update(void);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -385,16 +385,6 @@ dvr_autorec_entry_class_channel_get(void *o)
|
|||
return &ret;
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
dvr_autorec_entry_class_channel_list(void *o)
|
||||
{
|
||||
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");
|
||||
return m;
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_autorec_entry_class_title_set(void *o, const void *v)
|
||||
{
|
||||
|
@ -477,14 +467,6 @@ dvr_autorec_entry_class_start_set(void *o, const void *v)
|
|||
return dvr_autorec_entry_class_time_set(o, v, &dae->dae_start);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int
|
||||
dvr_autorec_entry_class_stop_set(void *o, const void *v)
|
||||
{
|
||||
return dvr_autorec_entry_class_time_set(o, v, &dae->dae_stop);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const void *
|
||||
dvr_autorec_entry_class_time_get(void *o, int tm)
|
||||
{
|
||||
|
@ -505,22 +487,13 @@ dvr_autorec_entry_class_start_get(void *o)
|
|||
return dvr_autorec_entry_class_time_get(o, dae->dae_start);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int
|
||||
dvr_autorec_entry_class_stop_get(void *o)
|
||||
{
|
||||
dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)o;
|
||||
return dvr_autorec_entry_class_time_get(o, v, &dae->dae_stop);
|
||||
}
|
||||
#endif
|
||||
|
||||
static htsmsg_t *
|
||||
dvr_autorec_entry_class_time_list(void *o)
|
||||
htsmsg_t *
|
||||
dvr_autorec_entry_class_time_list(void *o, const char *null)
|
||||
{
|
||||
int i;
|
||||
htsmsg_t *l = htsmsg_create_list();
|
||||
char buf[16];
|
||||
htsmsg_add_str(l, NULL, "Any");
|
||||
htsmsg_add_str(l, NULL, null);
|
||||
for (i = 0; i < 24*60; i += 10) {
|
||||
snprintf(buf, sizeof(buf), "%02d:%02d", i / 60, (i % 60));
|
||||
htsmsg_add_str(l, NULL, buf);
|
||||
|
@ -528,6 +501,12 @@ dvr_autorec_entry_class_time_list(void *o)
|
|||
return l;
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
dvr_autorec_entry_class_time_list_(void *o)
|
||||
{
|
||||
return dvr_autorec_entry_class_time_list(o, "Any");
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
dvr_autorec_entry_class_minduration_list(void *o)
|
||||
{
|
||||
|
@ -597,27 +576,26 @@ static const struct strtab dvr_autorec_entry_class_weekdays_tab[] = {
|
|||
{ "Sun", 7 },
|
||||
};
|
||||
|
||||
static htsmsg_t *
|
||||
htsmsg_t *
|
||||
dvr_autorec_entry_class_weekdays_list ( void *o )
|
||||
{
|
||||
return strtab2htsmsg(dvr_autorec_entry_class_weekdays_tab);
|
||||
}
|
||||
|
||||
static char *
|
||||
dvr_autorec_entry_class_weekdays_rend(void *o)
|
||||
char *
|
||||
dvr_autorec_entry_class_weekdays_rend(uint32_t weekdays)
|
||||
{
|
||||
dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)o;
|
||||
char buf[32];
|
||||
size_t l;
|
||||
int i;
|
||||
if (dae->dae_weekdays == 0x7f)
|
||||
if (weekdays == 0x7f)
|
||||
strcpy(buf + 1, "All days");
|
||||
else if (dae->dae_weekdays == 0)
|
||||
else if (weekdays == 0)
|
||||
strcpy(buf + 1, "No days");
|
||||
else {
|
||||
buf[0] = '\0';
|
||||
for (i = 0; i < 7; i++)
|
||||
if (dae->dae_weekdays & (1 << i)) {
|
||||
if (weekdays & (1 << i)) {
|
||||
l = strlen(buf);
|
||||
snprintf(buf + l, sizeof(buf) - l, ",%s",
|
||||
val2str(i + 1, dvr_autorec_entry_class_weekdays_tab));
|
||||
|
@ -626,6 +604,13 @@ dvr_autorec_entry_class_weekdays_rend(void *o)
|
|||
return strdup(buf + 1);
|
||||
}
|
||||
|
||||
static char *
|
||||
dvr_autorec_entry_class_weekdays_rend_(void *o)
|
||||
{
|
||||
dvr_autorec_entry_t *dae = (dvr_autorec_entry_t *)o;
|
||||
return dvr_autorec_entry_class_weekdays_rend(dae->dae_weekdays);
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_autorec_entry_class_brand_set(void *o, const void *v)
|
||||
{
|
||||
|
@ -776,7 +761,7 @@ const idclass_t dvr_autorec_entry_class = {
|
|||
.name = "Channel",
|
||||
.set = dvr_autorec_entry_class_channel_set,
|
||||
.get = dvr_autorec_entry_class_channel_get,
|
||||
.list = dvr_autorec_entry_class_channel_list,
|
||||
.list = channel_class_get_list,
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
|
@ -792,7 +777,7 @@ const idclass_t dvr_autorec_entry_class = {
|
|||
.name = "Starting Around",
|
||||
.set = dvr_autorec_entry_class_start_set,
|
||||
.get = dvr_autorec_entry_class_start_get,
|
||||
.list = dvr_autorec_entry_class_time_list,
|
||||
.list = dvr_autorec_entry_class_time_list_,
|
||||
},
|
||||
{
|
||||
.type = PT_TIME,
|
||||
|
@ -816,7 +801,8 @@ const idclass_t dvr_autorec_entry_class = {
|
|||
.set = dvr_autorec_entry_class_weekdays_set,
|
||||
.get = dvr_autorec_entry_class_weekdays_get,
|
||||
.list = dvr_autorec_entry_class_weekdays_list,
|
||||
.rend = dvr_autorec_entry_class_weekdays_rend,
|
||||
.rend = dvr_autorec_entry_class_weekdays_rend_,
|
||||
.def.u32 = 0x7f
|
||||
},
|
||||
{
|
||||
.type = PT_INT,
|
||||
|
|
|
@ -74,6 +74,8 @@ dvr_entry_get_extra_time_pre( dvr_entry_t *de )
|
|||
{
|
||||
time_t extra = de->de_start_extra;
|
||||
|
||||
if (de->de_timerec)
|
||||
return 0;
|
||||
if (!extra_valid(extra)) {
|
||||
if (de->de_channel)
|
||||
extra = de->de_channel->ch_dvr_extra_time_pre;
|
||||
|
@ -88,6 +90,8 @@ dvr_entry_get_extra_time_post( dvr_entry_t *de )
|
|||
{
|
||||
time_t extra = de->de_stop_extra;
|
||||
|
||||
if (de->de_timerec)
|
||||
return 0;
|
||||
if (!extra_valid(extra)) {
|
||||
if (de->de_channel)
|
||||
extra = de->de_channel->ch_dvr_extra_time_post;
|
||||
|
@ -443,13 +447,14 @@ dvr_entry_create(const char *uuid, htsmsg_t *conf)
|
|||
/**
|
||||
* Create the event
|
||||
*/
|
||||
static dvr_entry_t *
|
||||
_dvr_entry_create(const char *config_uuid, epg_broadcast_t *e,
|
||||
dvr_entry_t *
|
||||
dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
|
||||
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_timerec_entry_t *dte,
|
||||
dvr_prio_t pri, int retention)
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
|
@ -497,6 +502,8 @@ _dvr_entry_create(const char *config_uuid, epg_broadcast_t *e,
|
|||
htsmsg_add_u32(conf, "broadcast", e->id);
|
||||
if (dae)
|
||||
htsmsg_add_str(conf, "autorec", idnode_uuid_as_str(&dae->dae_id));
|
||||
if (dte)
|
||||
htsmsg_add_str(conf, "timerec", idnode_uuid_as_str(&dte->dte_id));
|
||||
|
||||
de = dvr_entry_create(NULL, conf);
|
||||
|
||||
|
@ -535,11 +542,11 @@ dvr_entry_create_htsp(const char *config_uuid,
|
|||
dvr_config_t *cfg = dvr_config_find_by_uuid(config_uuid);
|
||||
if (!cfg)
|
||||
cfg = dvr_config_find_by_name(config_uuid);
|
||||
return _dvr_entry_create(cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL,
|
||||
return dvr_entry_create_(cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL,
|
||||
NULL,
|
||||
ch, start, stop, start_extra, stop_extra,
|
||||
title, description, lang, content_type,
|
||||
creator, dae, pri, retention);
|
||||
creator, dae, NULL, pri, retention);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -555,12 +562,12 @@ dvr_entry_create_by_event(const char *config_uuid,
|
|||
if(!e->channel || !e->episode || !e->episode->title)
|
||||
return NULL;
|
||||
|
||||
return _dvr_entry_create(config_uuid, e,
|
||||
return dvr_entry_create_(config_uuid, e,
|
||||
e->channel, e->start, e->stop,
|
||||
start_extra, stop_extra,
|
||||
NULL, NULL, NULL,
|
||||
LIST_FIRST(&e->episode->genre),
|
||||
creator, dae, pri, retention);
|
||||
creator, dae, NULL, pri, retention);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -637,6 +644,11 @@ dvr_entry_dec_ref(dvr_entry_t *de)
|
|||
if(de->de_autorec != NULL)
|
||||
LIST_REMOVE(de, de_autorec_link);
|
||||
|
||||
if (de->de_timerec) {
|
||||
de->de_timerec->dte_spawn = NULL;
|
||||
de->de_timerec = NULL;
|
||||
}
|
||||
|
||||
if(de->de_config != NULL)
|
||||
LIST_REMOVE(de, de_config_link);
|
||||
|
||||
|
@ -1272,16 +1284,6 @@ dvr_entry_class_channel_get(void *o)
|
|||
return &ret;
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
dvr_entry_class_channel_list(void *o)
|
||||
{
|
||||
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");
|
||||
return m;
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_entry_class_channel_name_set(void *o, const void *v)
|
||||
{
|
||||
|
@ -1417,6 +1419,40 @@ dvr_entry_class_autorec_get(void *o)
|
|||
return &ret;
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_entry_class_timerec_set(void *o, const void *v)
|
||||
{
|
||||
dvr_entry_t *de = (dvr_entry_t *)o;
|
||||
dvr_timerec_entry_t *dte;
|
||||
if (!dvr_entry_is_editable(de))
|
||||
return 0;
|
||||
dte = v ? dvr_timerec_find_by_uuid(v) : NULL;
|
||||
if (dte == NULL) {
|
||||
if (de->de_timerec) {
|
||||
de->de_timerec->dte_spawn = NULL;
|
||||
de->de_timerec = NULL;
|
||||
return 1;
|
||||
}
|
||||
} else if (de->de_timerec != dte) {
|
||||
de->de_timerec = dte;
|
||||
dte->dte_spawn = de;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const void *
|
||||
dvr_entry_class_timerec_get(void *o)
|
||||
{
|
||||
static const char *ret;
|
||||
dvr_entry_t *de = (dvr_entry_t *)o;
|
||||
if (de->de_timerec)
|
||||
ret = idnode_uuid_as_str(&de->de_timerec->dte_id);
|
||||
else
|
||||
ret = "";
|
||||
return &ret;
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_entry_class_broadcast_set(void *o, const void *v)
|
||||
{
|
||||
|
@ -1732,7 +1768,7 @@ const idclass_t dvr_entry_class = {
|
|||
.name = "Channel",
|
||||
.set = dvr_entry_class_channel_set,
|
||||
.get = dvr_entry_class_channel_get,
|
||||
.list = dvr_entry_class_channel_list,
|
||||
.list = channel_class_get_list,
|
||||
.get_opts = dvr_entry_class_start_opts,
|
||||
},
|
||||
{
|
||||
|
@ -1863,6 +1899,14 @@ const idclass_t dvr_entry_class = {
|
|||
.get = dvr_entry_class_autorec_get,
|
||||
.opts = PO_RDONLY,
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
.id = "timerec",
|
||||
.name = "Auto Time Record",
|
||||
.set = dvr_entry_class_timerec_set,
|
||||
.get = dvr_entry_class_timerec_get,
|
||||
.opts = PO_RDONLY,
|
||||
},
|
||||
{
|
||||
.type = PT_U32,
|
||||
.id = "content_type",
|
||||
|
@ -2755,8 +2799,10 @@ dvr_init(void)
|
|||
dvr_inotify_init();
|
||||
#endif
|
||||
dvr_autorec_init();
|
||||
dvr_timerec_init();
|
||||
dvr_db_load();
|
||||
dvr_autorec_update();
|
||||
dvr_timerec_update();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2778,4 +2824,5 @@ dvr_done(void)
|
|||
dvr_config_destroy(cfg, 0);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
dvr_autorec_done();
|
||||
dvr_timerec_done();
|
||||
}
|
||||
|
|
606
src/dvr/dvr_timerec.c
Normal file
606
src/dvr/dvr_timerec.c
Normal file
|
@ -0,0 +1,606 @@
|
|||
/*
|
||||
* tvheadend, Automatic time-based recording
|
||||
* Copyright (C) 2014 Jaroslav Kysela <perex@perex.cz>
|
||||
*
|
||||
* 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 <pthread.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "tvheadend.h"
|
||||
#include "settings.h"
|
||||
#include "dvr.h"
|
||||
#include "dtable.h"
|
||||
#include "epg.h"
|
||||
|
||||
static int dvr_timerec_in_init = 0;
|
||||
|
||||
struct dvr_timerec_entry_queue timerec_entries;
|
||||
|
||||
static gtimer_t dvr_timerec_timer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static time_t
|
||||
dvr_timerec_timecorrection(time_t clk, int hm, struct tm *tm)
|
||||
{
|
||||
time_t r;
|
||||
int isdst;
|
||||
|
||||
localtime_r(&clk, tm);
|
||||
tm->tm_min = hm % 60;
|
||||
tm->tm_hour = hm / 60;
|
||||
isdst = tm->tm_isdst;
|
||||
r = mktime(tm);
|
||||
if (tm->tm_isdst != isdst) {
|
||||
tm->tm_min = hm % 60;
|
||||
tm->tm_hour = hm / 60;
|
||||
r = mktime(tm);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink - and remove any unstarted
|
||||
*/
|
||||
static void
|
||||
dvr_timerec_purge_spawn(dvr_timerec_entry_t *dte)
|
||||
{
|
||||
dvr_entry_t *de = dte->dte_spawn;
|
||||
|
||||
if (de && de->de_timerec) {
|
||||
dte->dte_spawn = NULL;
|
||||
de->de_timerec = NULL;
|
||||
if (de->de_sched_state == DVR_SCHEDULED)
|
||||
dvr_entry_cancel(de);
|
||||
else
|
||||
dvr_entry_save(de);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Title
|
||||
*/
|
||||
static const char *
|
||||
dvr_timerec_title(dvr_timerec_entry_t *dte, struct tm *start)
|
||||
{
|
||||
static char buf[256];
|
||||
size_t len;
|
||||
|
||||
if (dte->dte_title == NULL)
|
||||
return "Unknown";
|
||||
len = strftime(buf, sizeof(buf) - 1, dte->dte_title, start);
|
||||
buf[len] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the timerec entry
|
||||
*/
|
||||
void
|
||||
dvr_timerec_check(dvr_timerec_entry_t *dte)
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
time_t start, stop;
|
||||
struct tm tm_start, tm_stop;
|
||||
const char *title;
|
||||
char buf[200];
|
||||
|
||||
if(dte->dte_enabled == 0 || dte->dte_weekdays == 0)
|
||||
goto fail;
|
||||
if(dte->dte_start < 0 || dte->dte_start >= 24*60 ||
|
||||
dte->dte_stop < 0 || dte->dte_stop >= 24*60)
|
||||
goto fail;
|
||||
if(dte->dte_start >= dte->dte_stop)
|
||||
goto fail;
|
||||
if(dte->dte_channel == NULL)
|
||||
goto fail;
|
||||
|
||||
if(dte->dte_weekdays != 0x7f) {
|
||||
localtime_r(&dispatch_clock, &tm_start);
|
||||
if(!((1 << ((tm_start.tm_wday ?: 7) - 1)) & dte->dte_weekdays))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
start = dvr_timerec_timecorrection(dispatch_clock, dte->dte_start, &tm_start);
|
||||
stop = dvr_timerec_timecorrection(dispatch_clock, dte->dte_stop, &tm_stop);
|
||||
|
||||
/* day boundary correction */
|
||||
if (start > stop)
|
||||
stop += 24 * 60 * 60;
|
||||
assert(start < stop);
|
||||
|
||||
/* if it's really in past, don't queue */
|
||||
if (stop < dispatch_clock - 3600)
|
||||
goto fail;
|
||||
|
||||
/* purge the old entry */
|
||||
de = dte->dte_spawn;
|
||||
if (de) {
|
||||
if (de->de_start == start && de->de_stop == stop)
|
||||
return;
|
||||
dvr_timerec_purge_spawn(dte);
|
||||
}
|
||||
|
||||
title = dvr_timerec_title(dte, &tm_start);
|
||||
snprintf(buf, sizeof(buf), "Time recording%s%s",
|
||||
dte->dte_creator ? " by: " : "",
|
||||
dte->dte_creator ?: "");
|
||||
de = dvr_entry_create_(dte->dte_config_name, NULL, dte->dte_channel,
|
||||
start, stop, 0, 0, title,
|
||||
NULL, NULL, NULL, buf,
|
||||
NULL, dte, dte->dte_pri, dte->dte_retention);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
dvr_timerec_purge_spawn(dte);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
dvr_timerec_entry_t *
|
||||
dvr_timerec_create(const char *uuid, htsmsg_t *conf)
|
||||
{
|
||||
dvr_timerec_entry_t *dte;
|
||||
|
||||
dte = calloc(1, sizeof(*dte));
|
||||
|
||||
if (idnode_insert(&dte->dte_id, uuid, &dvr_timerec_entry_class, 0)) {
|
||||
if (uuid)
|
||||
tvhwarn("dvr", "invalid timerec entry uuid '%s'", uuid);
|
||||
free(dte);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dte->dte_title = strdup("Time-%x-%R");
|
||||
dte->dte_weekdays = 0x7f;
|
||||
dte->dte_pri = DVR_PRIO_NORMAL;
|
||||
dte->dte_start = -1;
|
||||
dte->dte_stop = -1;
|
||||
|
||||
TAILQ_INSERT_TAIL(&timerec_entries, dte, dte_link);
|
||||
|
||||
idnode_load(&dte->dte_id, conf);
|
||||
|
||||
return dte;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
timerec_entry_destroy(dvr_timerec_entry_t *dte, int delconf)
|
||||
{
|
||||
dvr_timerec_purge_spawn(dte);
|
||||
|
||||
if (delconf)
|
||||
hts_settings_remove("dvr/timerec/%s", idnode_uuid_as_str(&dte->dte_id));
|
||||
|
||||
TAILQ_REMOVE(&timerec_entries, dte, dte_link);
|
||||
idnode_unlink(&dte->dte_id);
|
||||
|
||||
free(dte->dte_name);
|
||||
free(dte->dte_config_name);
|
||||
free(dte->dte_creator);
|
||||
free(dte->dte_comment);
|
||||
|
||||
if(dte->dte_channel != NULL)
|
||||
LIST_REMOVE(dte, dte_channel_link);
|
||||
|
||||
free(dte);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_timerec_save(dvr_timerec_entry_t *dte)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
idnode_save(&dte->dte_id, m);
|
||||
hts_settings_save(m, "dvr/timerec/%s", idnode_uuid_as_str(&dte->dte_id));
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* DVR Autorec Entry Class definition
|
||||
* **************************************************************************/
|
||||
|
||||
static void
|
||||
dvr_timerec_entry_class_save(idnode_t *self)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)self;
|
||||
dvr_timerec_save(dte);
|
||||
dvr_timerec_check(dte);
|
||||
}
|
||||
|
||||
static void
|
||||
dvr_timerec_entry_class_delete(idnode_t *self)
|
||||
{
|
||||
timerec_entry_destroy((dvr_timerec_entry_t *)self, 1);
|
||||
}
|
||||
|
||||
static const char *
|
||||
dvr_timerec_entry_class_get_title (idnode_t *self)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)self;
|
||||
const char *s = "";
|
||||
if (dte->dte_name && dte->dte_name[0] != '\0')
|
||||
s = dte->dte_name;
|
||||
else if (dte->dte_comment && dte->dte_comment[0] != '\0')
|
||||
s = dte->dte_comment;
|
||||
return s;
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_timerec_entry_class_channel_set(void *o, const void *v)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
channel_t *ch = v ? channel_find_by_uuid(v) : NULL;
|
||||
if (ch == NULL) ch = v ? channel_find_by_name(v) : NULL;
|
||||
if (ch == NULL) {
|
||||
if (dte->dte_channel) {
|
||||
LIST_REMOVE(dte, dte_channel_link);
|
||||
dte->dte_channel = NULL;
|
||||
return 1;
|
||||
}
|
||||
} else if (dte->dte_channel != ch) {
|
||||
if (dte->dte_channel)
|
||||
LIST_REMOVE(dte, dte_channel_link);
|
||||
dte->dte_channel = ch;
|
||||
LIST_INSERT_HEAD(&ch->ch_timerecs, dte, dte_channel_link);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const void *
|
||||
dvr_timerec_entry_class_channel_get(void *o)
|
||||
{
|
||||
static const char *ret;
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
if (dte->dte_channel)
|
||||
ret = idnode_uuid_as_str(&dte->dte_channel->ch_id);
|
||||
else
|
||||
ret = "";
|
||||
return &ret;
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_timerec_entry_class_time_set(void *o, const void *v, int *tm)
|
||||
{
|
||||
const char *s = v;
|
||||
int t;
|
||||
|
||||
if(s == NULL || s[0] == '\0' || !isdigit(s[0]))
|
||||
t = -1;
|
||||
else if(strchr(s, ':') != NULL)
|
||||
// formatted time string - convert
|
||||
t = (atoi(s) * 60) + atoi(s + 3);
|
||||
else {
|
||||
t = atoi(s);
|
||||
}
|
||||
if (t >= 24 * 60)
|
||||
t = -1;
|
||||
if (t != *tm) {
|
||||
*tm = t;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_timerec_entry_class_start_set(void *o, const void *v)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
return dvr_timerec_entry_class_time_set(o, v, &dte->dte_start);
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_timerec_entry_class_stop_set(void *o, const void *v)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
return dvr_timerec_entry_class_time_set(o, v, &dte->dte_stop);
|
||||
}
|
||||
|
||||
static const void *
|
||||
dvr_timerec_entry_class_time_get(void *o, int tm)
|
||||
{
|
||||
static const char *ret;
|
||||
static char buf[16];
|
||||
if (tm >= 0)
|
||||
snprintf(buf, sizeof(buf), "%02d:%02d", tm / 60, tm % 60);
|
||||
else
|
||||
strcpy(buf, "Any");
|
||||
ret = buf;
|
||||
return &ret;
|
||||
}
|
||||
|
||||
static const void *
|
||||
dvr_timerec_entry_class_start_get(void *o)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
return dvr_timerec_entry_class_time_get(o, dte->dte_start);
|
||||
}
|
||||
|
||||
static const void *
|
||||
dvr_timerec_entry_class_stop_get(void *o)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
return dvr_timerec_entry_class_time_get(o, dte->dte_stop);
|
||||
}
|
||||
|
||||
static htsmsg_t *
|
||||
dvr_timerec_entry_class_time_list(void *o)
|
||||
{
|
||||
return dvr_autorec_entry_class_time_list(o, "Invalid");
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_timerec_entry_class_config_name_set(void *o, const void *v)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
dvr_config_t *cfg = v ? dvr_config_find_by_uuid(v) : NULL;
|
||||
if (cfg == NULL) cfg = v ? dvr_config_find_by_name_default(v): NULL;
|
||||
if (cfg == NULL && dte->dte_config_name) {
|
||||
free(dte->dte_config_name);
|
||||
return 1;
|
||||
} else if (strcmp(dte->dte_config_name ?: "", cfg ? cfg->dvr_config_name : "")) {
|
||||
free(dte->dte_config_name);
|
||||
dte->dte_config_name = strdup(cfg->dvr_config_name);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dvr_timerec_entry_class_weekdays_set(void *o, const void *v)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
htsmsg_field_t *f;
|
||||
uint32_t u32, bits = 0;
|
||||
|
||||
HTSMSG_FOREACH(f, (htsmsg_t *)v)
|
||||
if (!htsmsg_field_get_u32(f, &u32) && u32 > 0 && u32 < 8)
|
||||
bits |= (1 << (u32 - 1));
|
||||
|
||||
if (bits != dte->dte_weekdays) {
|
||||
dte->dte_weekdays = bits;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const void *
|
||||
dvr_timerec_entry_class_weekdays_get(void *o)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
htsmsg_t *m = htsmsg_create_list();
|
||||
int i;
|
||||
for (i = 0; i < 7; i++)
|
||||
if (dte->dte_weekdays & (1 << i))
|
||||
htsmsg_add_u32(m, NULL, i + 1);
|
||||
return m;
|
||||
}
|
||||
|
||||
static char *
|
||||
dvr_timerec_entry_class_weekdays_rend(void *o)
|
||||
{
|
||||
dvr_timerec_entry_t *dte = (dvr_timerec_entry_t *)o;
|
||||
return dvr_autorec_entry_class_weekdays_rend(dte->dte_weekdays);
|
||||
}
|
||||
|
||||
const idclass_t dvr_timerec_entry_class = {
|
||||
.ic_class = "dvrtimerec",
|
||||
.ic_caption = "DVR Time-Record Entry",
|
||||
.ic_event = "dvrtimerec",
|
||||
.ic_save = dvr_timerec_entry_class_save,
|
||||
.ic_get_title = dvr_timerec_entry_class_get_title,
|
||||
.ic_delete = dvr_timerec_entry_class_delete,
|
||||
.ic_properties = (const property_t[]) {
|
||||
{
|
||||
.type = PT_BOOL,
|
||||
.id = "enabled",
|
||||
.name = "Enabled",
|
||||
.off = offsetof(dvr_timerec_entry_t, dte_enabled),
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
.id = "name",
|
||||
.name = "Name",
|
||||
.off = offsetof(dvr_timerec_entry_t, dte_name),
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
.id = "title",
|
||||
.name = "Title",
|
||||
.off = offsetof(dvr_timerec_entry_t, dte_title),
|
||||
.def.s = "Time-%x-%R",
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
.id = "channel",
|
||||
.name = "Channel",
|
||||
.set = dvr_timerec_entry_class_channel_set,
|
||||
.get = dvr_timerec_entry_class_channel_get,
|
||||
.list = channel_class_get_list,
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
.id = "start",
|
||||
.name = "Start",
|
||||
.set = dvr_timerec_entry_class_start_set,
|
||||
.get = dvr_timerec_entry_class_start_get,
|
||||
.list = dvr_timerec_entry_class_time_list,
|
||||
.def.s = "12:00",
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
.id = "stop",
|
||||
.name = "Stop",
|
||||
.set = dvr_timerec_entry_class_stop_set,
|
||||
.get = dvr_timerec_entry_class_stop_get,
|
||||
.list = dvr_timerec_entry_class_time_list,
|
||||
.def.s = "12:00",
|
||||
},
|
||||
{
|
||||
.type = PT_U32,
|
||||
.islist = 1,
|
||||
.id = "weekdays",
|
||||
.name = "Week Days",
|
||||
.set = dvr_timerec_entry_class_weekdays_set,
|
||||
.get = dvr_timerec_entry_class_weekdays_get,
|
||||
.list = dvr_autorec_entry_class_weekdays_list,
|
||||
.rend = dvr_timerec_entry_class_weekdays_rend,
|
||||
.def.u32 = 0x7f
|
||||
},
|
||||
{
|
||||
.type = PT_U32,
|
||||
.id = "pri",
|
||||
.name = "Priority",
|
||||
.list = dvr_entry_class_pri_list,
|
||||
.def.i = DVR_PRIO_NORMAL,
|
||||
.off = offsetof(dvr_timerec_entry_t, dte_pri),
|
||||
},
|
||||
{
|
||||
.type = PT_INT,
|
||||
.id = "retention",
|
||||
.name = "Retention",
|
||||
.off = offsetof(dvr_timerec_entry_t, dte_retention),
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
.id = "config_name",
|
||||
.name = "DVR Configuration",
|
||||
.set = dvr_timerec_entry_class_config_name_set,
|
||||
.list = dvr_entry_class_config_name_list,
|
||||
.off = offsetof(dvr_timerec_entry_t, dte_config_name),
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
.id = "creator",
|
||||
.name = "Creator",
|
||||
.off = offsetof(dvr_timerec_entry_t, dte_creator),
|
||||
.opts = PO_RDONLY,
|
||||
},
|
||||
{
|
||||
.type = PT_STR,
|
||||
.id = "comment",
|
||||
.name = "Comment",
|
||||
.off = offsetof(dvr_timerec_entry_t, dte_comment),
|
||||
},
|
||||
{}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_timerec_init(void)
|
||||
{
|
||||
htsmsg_t *l, *c;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
TAILQ_INIT(&timerec_entries);
|
||||
dvr_timerec_in_init = 1;
|
||||
if((l = hts_settings_load("dvr/timerec")) != NULL) {
|
||||
HTSMSG_FOREACH(f, l) {
|
||||
if((c = htsmsg_get_map_by_field(f)) == NULL)
|
||||
continue;
|
||||
(void)dvr_timerec_create(f->hmf_name, c);
|
||||
}
|
||||
htsmsg_destroy(l);
|
||||
}
|
||||
dvr_timerec_in_init = 0;
|
||||
}
|
||||
|
||||
void
|
||||
dvr_timerec_done(void)
|
||||
{
|
||||
dvr_timerec_entry_t *dte;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
while ((dte = TAILQ_FIRST(&timerec_entries)) != NULL)
|
||||
timerec_entry_destroy(dte, 0);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
dvr_timerec_timer_cb(void *aux)
|
||||
{
|
||||
dvr_timerec_entry_t *dte;
|
||||
time_t next;
|
||||
struct tm tm;
|
||||
|
||||
tvhtrace("dvr", "timerec update");
|
||||
|
||||
/* check all entries */
|
||||
TAILQ_FOREACH(dte, &timerec_entries, dte_link)
|
||||
dvr_timerec_check(dte);
|
||||
|
||||
/* load the timer */
|
||||
next = dispatch_clock + 60 * 60 * 24; /* next day */
|
||||
next = dvr_timerec_timecorrection(next, 0 /* midnight */, &tm);
|
||||
tvhtrace("dvr", "next timerec check scheduled in %li seconds", (long)(next - dispatch_clock));
|
||||
gtimer_arm(&dvr_timerec_timer, dvr_timerec_timer_cb, NULL, next);
|
||||
}
|
||||
|
||||
void
|
||||
dvr_timerec_update(void)
|
||||
{
|
||||
/* check all timerec entries and load the timer */
|
||||
dvr_timerec_timer_cb(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
timerec_destroy_by_channel(channel_t *ch, int delconf)
|
||||
{
|
||||
dvr_timerec_entry_t *dte;
|
||||
|
||||
while((dte = LIST_FIRST(&ch->ch_timerecs)) != NULL)
|
||||
timerec_entry_destroy(dte, delconf);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
timerec_destroy_by_id(const char *id, int delconf)
|
||||
{
|
||||
dvr_timerec_entry_t *dte;
|
||||
dte = dvr_timerec_find_by_uuid(id);
|
||||
|
||||
if (dte)
|
||||
timerec_entry_destroy(dte, delconf);
|
||||
}
|
|
@ -193,6 +193,7 @@ LIST_HEAD(th_descrambler_list, th_descrambler);
|
|||
TAILQ_HEAD(th_refpkt_queue, th_refpkt);
|
||||
TAILQ_HEAD(th_muxpkt_queue, th_muxpkt);
|
||||
LIST_HEAD(dvr_autorec_entry_list, dvr_autorec_entry);
|
||||
LIST_HEAD(dvr_timerec_entry_list, dvr_timerec_entry);
|
||||
TAILQ_HEAD(th_pktref_queue, th_pktref);
|
||||
LIST_HEAD(streaming_target_list, streaming_target);
|
||||
|
||||
|
|
|
@ -77,6 +77,30 @@ tvheadend.dvrRowActions = function() {
|
|||
});
|
||||
}
|
||||
|
||||
tvheadend.weekdaysRenderer = function(v) {
|
||||
var t = [];
|
||||
var d = v.push ? v : [v];
|
||||
if (d.length == 7) {
|
||||
v = "All days";
|
||||
} else if (d.length == 0) {
|
||||
v = "No days";
|
||||
} else {
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var r = st.find('key', d[i]);
|
||||
if (r !== -1) {
|
||||
var nv = st.getAt(r).get('val');
|
||||
if (nv)
|
||||
t.push(nv);
|
||||
} else {
|
||||
t.push(d[i]);
|
||||
}
|
||||
}
|
||||
v = t.join(',');
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -326,6 +350,7 @@ tvheadend.autorec_editor = function(panel, index) {
|
|||
tabIndex: index,
|
||||
columns: {
|
||||
enabled: { width: 50 },
|
||||
name: { width: 200 },
|
||||
title: { width: 300 },
|
||||
channel: { width: 200 },
|
||||
tag: { width: 200 },
|
||||
|
@ -342,44 +367,21 @@ tvheadend.autorec_editor = function(panel, index) {
|
|||
add: {
|
||||
url: 'api/dvr/autorec',
|
||||
params: {
|
||||
list: 'enabled,title,channel,tag,content_type,minduration,' +
|
||||
list: 'enabled,name,title,channel,tag,content_type,minduration,' +
|
||||
'maxduration,weekdays,start,pri,config_name,comment',
|
||||
},
|
||||
create: { }
|
||||
},
|
||||
del: true,
|
||||
list: 'enabled,title,channel,tag,content_type,minduration,' +
|
||||
list: 'enabled,name,title,channel,tag,content_type,minduration,' +
|
||||
'maxduration,weekdays,start,pri,config_name,creator,comment',
|
||||
columns: {
|
||||
weekdays: {
|
||||
renderer: function(st) {
|
||||
return function(v) {
|
||||
var t = [];
|
||||
var d = v.push ? v : [v];
|
||||
if (d.length == 7) {
|
||||
v = "All days";
|
||||
} else if (d.length == 0) {
|
||||
v = "No days";
|
||||
} else {
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var r = st.find('key', d[i]);
|
||||
if (r !== -1) {
|
||||
var nv = st.getAt(r).get('val');
|
||||
if (nv)
|
||||
t.push(nv);
|
||||
} else {
|
||||
t.push(d[i]);
|
||||
}
|
||||
}
|
||||
v = t.join(',');
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
renderer: function(st) { return tvheadend.weekdaysRenderer; }
|
||||
}
|
||||
},
|
||||
sort: {
|
||||
field: 'title',
|
||||
field: 'name',
|
||||
direction: 'ASC'
|
||||
},
|
||||
help: function() {
|
||||
|
@ -391,6 +393,57 @@ tvheadend.autorec_editor = function(panel, index) {
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
tvheadend.timerec_editor = function(panel, index) {
|
||||
|
||||
tvheadend.idnode_grid(panel, {
|
||||
url: 'api/dvr/timerec',
|
||||
titleS: 'DVR TimeRec Entry',
|
||||
titleP: 'DVR TimeRec Entries',
|
||||
iconCls: 'clock',
|
||||
tabIndex: index,
|
||||
columns: {
|
||||
enabled: { width: 50 },
|
||||
name: { width: 200 },
|
||||
title: { width: 300 },
|
||||
channel: { width: 200 },
|
||||
weekdays: { width: 160 },
|
||||
start: { width: 100 },
|
||||
stop: { width: 100 },
|
||||
pri: { width: 80 },
|
||||
config_name: { width: 120 },
|
||||
creator: { width: 200 },
|
||||
comment: { width: 200 },
|
||||
},
|
||||
add: {
|
||||
url: 'api/dvr/timerec',
|
||||
params: {
|
||||
list: 'enabled,name,title,channel,weekdays,start,stop,pri,config_name,comment',
|
||||
},
|
||||
create: { }
|
||||
},
|
||||
del: true,
|
||||
list: 'enabled,name,title,channel,weekdays,start,stop,pri,config_name,comment',
|
||||
columns: {
|
||||
weekdays: {
|
||||
renderer: function(st) { return tvheadend.weekdaysRenderer; }
|
||||
}
|
||||
},
|
||||
sort: {
|
||||
field: 'name',
|
||||
direction: 'ASC'
|
||||
},
|
||||
help: function() {
|
||||
new tvheadend.help('DVR', 'config_dvrtime.html');
|
||||
},
|
||||
});
|
||||
|
||||
return panel;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -406,5 +459,6 @@ tvheadend.dvr = function(panel, index) {
|
|||
tvheadend.dvr_finished(p, 1);
|
||||
tvheadend.dvr_failed(p, 2);
|
||||
tvheadend.autorec_editor(p, 3);
|
||||
tvheadend.timerec_editor(p, 4);
|
||||
return p;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue