* Inject entries in DVR schedule as soon as we know about an EPG
entry that matches an autorecording rule. Previously Tvheadend would scan the EPG continously and just grab shows as they neared air time. The drawbacks of this approach was that it's a bit hard to understand what is happening. It also makes (more) correct wakeup from suspend hard to do.
This commit is contained in:
parent
727b590771
commit
85fc302882
8 changed files with 138 additions and 78 deletions
7
debian/changelog
vendored
7
debian/changelog
vendored
|
@ -8,6 +8,13 @@ hts-tvheadend (2.11-WIP) hts; urgency=low
|
|||
written to disk as Tvheadend tunes to it. This should aid a lot when
|
||||
it comes to debugging
|
||||
|
||||
* Inject entries in DVR schedule as soon as we know about an EPG
|
||||
entry that matches an autorecording rule. Previously Tvheadend
|
||||
would scan the EPG continously and just grab shows as they neared
|
||||
air time. The drawbacks of this approach was that it's a bit hard
|
||||
to understand what is happening. It also makes (more) correct
|
||||
wakeup from suspend hard to do.
|
||||
|
||||
hts-tvheadend (2.10) hts; urgency=high
|
||||
|
||||
* Fix a crash in HTSP server.
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#define DVR_H
|
||||
|
||||
#include <libavformat/avformat.h>
|
||||
#include <regex.h>
|
||||
#include "epg.h"
|
||||
#include "channels.h"
|
||||
#include "subscriptions.h"
|
||||
|
@ -96,6 +97,12 @@ typedef struct dvr_entry {
|
|||
|
||||
uint32_t de_dont_reschedule;
|
||||
|
||||
/**
|
||||
* Autorec linkage
|
||||
*/
|
||||
LIST_ENTRY(dvr_entry) de_autorec_link;
|
||||
struct dvr_autorec_entry *de_autorec;
|
||||
|
||||
/**
|
||||
* Fields for recording
|
||||
*/
|
||||
|
@ -129,14 +136,47 @@ typedef struct dvr_entry {
|
|||
|
||||
} dvr_entry_t;
|
||||
|
||||
|
||||
/**
|
||||
* Autorec entry
|
||||
*/
|
||||
typedef struct dvr_autorec_entry {
|
||||
TAILQ_ENTRY(dvr_autorec_entry) dae_link;
|
||||
char *dae_id;
|
||||
|
||||
int dae_enabled;
|
||||
char *dae_creator;
|
||||
char *dae_comment;
|
||||
|
||||
char *dae_title;
|
||||
regex_t dae_title_preg;
|
||||
|
||||
epg_content_group_t *dae_ecg;
|
||||
|
||||
int dae_weekdays;
|
||||
|
||||
channel_t *dae_channel;
|
||||
LIST_ENTRY(dvr_autorec_entry) dae_channel_link;
|
||||
|
||||
channel_tag_t *dae_channel_tag;
|
||||
LIST_ENTRY(dvr_autorec_entry) dae_channel_tag_link;
|
||||
|
||||
struct dvr_entry_list dae_spawns;
|
||||
|
||||
} dvr_autorec_entry_t;
|
||||
|
||||
|
||||
/**
|
||||
* Prototypes
|
||||
*/
|
||||
dvr_entry_t *dvr_entry_create_by_event(event_t *e, const char *creator);
|
||||
void dvr_entry_create_by_autorec(event_t *e, dvr_autorec_entry_t *dae);
|
||||
|
||||
dvr_entry_t *dvr_entry_create_by_event(event_t *e, const char *creator,
|
||||
dvr_autorec_entry_t *dae);
|
||||
|
||||
dvr_entry_t *dvr_entry_create(channel_t *ch, time_t start, time_t stop,
|
||||
const char *title, const char *description,
|
||||
const char *creator);
|
||||
const char *creator, dvr_autorec_entry_t *dae);
|
||||
|
||||
void dvr_init(void);
|
||||
|
||||
|
@ -190,8 +230,10 @@ void dvr_autorec_add(const char *title, const char *channel,
|
|||
const char *tag, const char *contentgrp,
|
||||
const char *creator, const char *comment);
|
||||
|
||||
void dvr_autorec_check(event_t *e);
|
||||
void dvr_autorec_check_event(event_t *e);
|
||||
|
||||
void autorec_destroy_by_channel(channel_t *ch);
|
||||
|
||||
dvr_autorec_entry_t *autorec_entry_find(const char *id, int create);
|
||||
|
||||
#endif /* DVR_H */
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <regex.h>
|
||||
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "settings.h"
|
||||
|
@ -41,31 +39,22 @@ TAILQ_HEAD(dvr_autorec_entry_queue, dvr_autorec_entry);
|
|||
|
||||
struct dvr_autorec_entry_queue autorec_entries;
|
||||
|
||||
typedef struct dvr_autorec_entry {
|
||||
TAILQ_ENTRY(dvr_autorec_entry) dae_link;
|
||||
char *dae_id;
|
||||
static void dvr_autorec_changed(dvr_autorec_entry_t *dae);
|
||||
|
||||
int dae_enabled;
|
||||
char *dae_creator;
|
||||
char *dae_comment;
|
||||
|
||||
char *dae_title;
|
||||
regex_t dae_title_preg;
|
||||
|
||||
epg_content_group_t *dae_ecg;
|
||||
|
||||
int dae_weekdays;
|
||||
|
||||
channel_t *dae_channel;
|
||||
LIST_ENTRY(dvr_autorec_entry) dae_channel_link;
|
||||
|
||||
channel_tag_t *dae_channel_tag;
|
||||
LIST_ENTRY(dvr_autorec_entry) dae_channel_tag_link;
|
||||
|
||||
} dvr_autorec_entry_t;
|
||||
|
||||
static void dvr_autorec_check_just_enabled(dvr_autorec_entry_t *dae);
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_autorec_purge_spawns(dvr_autorec_entry_t *dae)
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
|
||||
while((de = LIST_FIRST(&dae->dae_spawns)) != NULL) {
|
||||
LIST_REMOVE(de, de_autorec_link);
|
||||
de->de_autorec = NULL;
|
||||
dvr_entry_cancel(de);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -98,9 +87,11 @@ autorec_cmp(dvr_autorec_entry_t *dae, event_t *e)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if(dae->dae_title != NULL && e->e_title != NULL &&
|
||||
regexec(&dae->dae_title_preg, e->e_title, 0, NULL, 0))
|
||||
if(dae->dae_title != NULL) {
|
||||
if(e->e_title == NULL ||
|
||||
regexec(&dae->dae_title_preg, e->e_title, 0, NULL, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(dae->dae_weekdays != 0x7f) {
|
||||
struct tm tm;
|
||||
|
@ -115,7 +106,7 @@ autorec_cmp(dvr_autorec_entry_t *dae, event_t *e)
|
|||
/**
|
||||
*
|
||||
*/
|
||||
static dvr_autorec_entry_t *
|
||||
dvr_autorec_entry_t *
|
||||
autorec_entry_find(const char *id, int create)
|
||||
{
|
||||
dvr_autorec_entry_t *dae;
|
||||
|
@ -154,6 +145,8 @@ autorec_entry_find(const char *id, int create)
|
|||
static void
|
||||
autorec_entry_destroy(dvr_autorec_entry_t *dae)
|
||||
{
|
||||
dvr_autorec_purge_spawns(dae);
|
||||
|
||||
free(dae->dae_id);
|
||||
|
||||
free(dae->dae_creator);
|
||||
|
@ -293,8 +286,11 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values,
|
|||
if((dae = autorec_entry_find(id, maycreate)) == NULL)
|
||||
return NULL;
|
||||
|
||||
tvh_str_set(&dae->dae_creator, htsmsg_get_str(values, "creator"));
|
||||
tvh_str_set(&dae->dae_comment, htsmsg_get_str(values, "comment"));
|
||||
printf("autorec_record_update\n");
|
||||
htsmsg_print(values);
|
||||
|
||||
tvh_str_update(&dae->dae_creator, htsmsg_get_str(values, "creator"));
|
||||
tvh_str_update(&dae->dae_comment, htsmsg_get_str(values, "comment"));
|
||||
|
||||
if((s = htsmsg_get_str(values, "channel")) != NULL) {
|
||||
if(dae->dae_channel != NULL) {
|
||||
|
@ -340,8 +336,7 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values,
|
|||
if(!htsmsg_get_u32(values, "enabled", &u32))
|
||||
dae->dae_enabled = u32;
|
||||
|
||||
if(dae->dae_enabled)
|
||||
dvr_autorec_check_just_enabled(dae);
|
||||
dvr_autorec_changed(dae);
|
||||
|
||||
return autorec_record_build(dae);
|
||||
}
|
||||
|
@ -435,19 +430,7 @@ dvr_autorec_add(const char *title, const char *channel,
|
|||
htsmsg_add_u32(m, "asyncreload", 1);
|
||||
notify_by_msg("autorec", m);
|
||||
|
||||
dvr_autorec_check_just_enabled(dae);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
autorec_schedule(event_t *e, dvr_autorec_entry_t *dae)
|
||||
{
|
||||
char buf[200];
|
||||
|
||||
snprintf(buf, sizeof(buf), "Auto recording by: %s", dae->dae_creator);
|
||||
dvr_entry_create_by_event(e, buf);
|
||||
dvr_autorec_changed(dae);
|
||||
}
|
||||
|
||||
|
||||
|
@ -455,37 +438,31 @@ autorec_schedule(event_t *e, dvr_autorec_entry_t *dae)
|
|||
*
|
||||
*/
|
||||
void
|
||||
dvr_autorec_check(event_t *e)
|
||||
dvr_autorec_check_event(event_t *e)
|
||||
{
|
||||
dvr_autorec_entry_t *dae;
|
||||
|
||||
TAILQ_FOREACH(dae, &autorec_entries, dae_link)
|
||||
if(autorec_cmp(dae, e))
|
||||
autorec_schedule(e, dae);
|
||||
|
||||
if((e = RB_NEXT(e, e_channel_link)) == NULL)
|
||||
return;
|
||||
|
||||
/* Check next event too */
|
||||
TAILQ_FOREACH(dae, &autorec_entries, dae_link)
|
||||
if(autorec_cmp(dae, e))
|
||||
autorec_schedule(e, dae);
|
||||
dvr_entry_create_by_autorec(e, dae);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
dvr_autorec_check_just_enabled(dvr_autorec_entry_t *dae)
|
||||
dvr_autorec_changed(dvr_autorec_entry_t *dae)
|
||||
{
|
||||
channel_t *ch;
|
||||
event_t *e;
|
||||
|
||||
dvr_autorec_purge_spawns(dae);
|
||||
|
||||
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
|
||||
if(ch->ch_epg_current == NULL)
|
||||
continue;
|
||||
|
||||
if(autorec_cmp(dae, ch->ch_epg_current))
|
||||
autorec_schedule(ch->ch_epg_current, dae);
|
||||
RB_FOREACH(e, &ch->ch_epg_events, e_channel_link) {
|
||||
if(autorec_cmp(dae, e))
|
||||
dvr_entry_create_by_autorec(e, dae);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ dvr_entry_link(dvr_entry_t *de)
|
|||
dvr_entry_t *
|
||||
dvr_entry_create(channel_t *ch, time_t start, time_t stop,
|
||||
const char *title, const char *description,
|
||||
const char *creator)
|
||||
const char *creator, dvr_autorec_entry_t *dae)
|
||||
{
|
||||
dvr_entry_t *de;
|
||||
char tbuf[30];
|
||||
|
@ -174,6 +174,9 @@ dvr_entry_create(channel_t *ch, time_t start, time_t stop,
|
|||
localtime_r(&t, &tm);
|
||||
strftime(tbuf, sizeof(tbuf), "%c", &tm);
|
||||
|
||||
de->de_autorec = dae;
|
||||
LIST_INSERT_HEAD(&dae->dae_spawns, de, de_autorec_link);
|
||||
|
||||
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" starting at %s, "
|
||||
"scheduled for recording by \"%s\"",
|
||||
de->de_title, de->de_channel->ch_name, tbuf, creator);
|
||||
|
@ -188,13 +191,27 @@ dvr_entry_create(channel_t *ch, time_t start, time_t stop,
|
|||
*
|
||||
*/
|
||||
dvr_entry_t *
|
||||
dvr_entry_create_by_event(event_t *e, const char *creator)
|
||||
dvr_entry_create_by_event(event_t *e, const char *creator,
|
||||
dvr_autorec_entry_t *dae)
|
||||
{
|
||||
if(e->e_channel == NULL || e->e_title == NULL)
|
||||
return NULL;
|
||||
|
||||
return dvr_entry_create(e->e_channel, e->e_start, e->e_stop,
|
||||
e->e_title, e->e_desc, creator);
|
||||
e->e_title, e->e_desc, creator, dae);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
dvr_entry_create_by_autorec(event_t *e, dvr_autorec_entry_t *dae)
|
||||
{
|
||||
char buf[200];
|
||||
|
||||
snprintf(buf, sizeof(buf), "Auto recording by: %s", dae->dae_creator);
|
||||
dvr_entry_create_by_event(e, buf, dae);
|
||||
}
|
||||
|
||||
|
||||
|
@ -211,6 +228,9 @@ dvr_entry_dec_ref(dvr_entry_t *de)
|
|||
return;
|
||||
}
|
||||
|
||||
if(de->de_autorec != NULL)
|
||||
LIST_REMOVE(de, de_autorec_link);
|
||||
|
||||
free(de->de_creator);
|
||||
free(de->de_title);
|
||||
free(de->de_ititle);
|
||||
|
@ -301,6 +321,17 @@ dvr_db_load_one(htsmsg_t *c, int id)
|
|||
tvh_str_set(&de->de_error, htsmsg_get_str(c, "error"));
|
||||
|
||||
htsmsg_get_u32(c, "noresched", &de->de_dont_reschedule);
|
||||
|
||||
s = htsmsg_get_str(c, "autorec");
|
||||
if(s != NULL) {
|
||||
dvr_autorec_entry_t *dae = autorec_entry_find(s, 0);
|
||||
|
||||
if(dae != NULL) {
|
||||
de->de_autorec = dae;
|
||||
LIST_INSERT_HEAD(&dae->dae_spawns, de, de_autorec_link);
|
||||
}
|
||||
}
|
||||
|
||||
dvr_entry_link(de);
|
||||
}
|
||||
|
||||
|
@ -359,6 +390,9 @@ dvr_entry_save(dvr_entry_t *de)
|
|||
|
||||
htsmsg_add_u32(m, "noresched", de->de_dont_reschedule);
|
||||
|
||||
if(de->de_autorec != NULL)
|
||||
htsmsg_add_str(m, "autorec", de->de_autorec->dae_id);
|
||||
|
||||
hts_settings_save(m, "dvr/log/%d", de->de_id);
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
@ -589,10 +623,9 @@ dvr_init(void)
|
|||
dvr_storage);
|
||||
}
|
||||
|
||||
dvr_db_load();
|
||||
|
||||
dvr_autorec_init();
|
||||
|
||||
dvr_db_load();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -611,7 +644,7 @@ dvr_save(void)
|
|||
htsmsg_add_u32(m, "channel-in-title", !!(dvr_flags & DVR_CHANNEL_IN_TITLE));
|
||||
htsmsg_add_u32(m, "date-in-title", !!(dvr_flags & DVR_DATE_IN_TITLE));
|
||||
htsmsg_add_u32(m, "time-in-title", !!(dvr_flags & DVR_TIME_IN_TITLE));
|
||||
htsmsg_add_u32(m, "whitespace-in-title", !!(dvr_flags & DVR_WHITESPACE_IN_TITLE));
|
||||
htsmsg_add_u32(m, "whitespace-in-title", !!(dvr_flags & DVR_WHITESPACE_IN_TITLE));
|
||||
if(dvr_postproc != NULL)
|
||||
htsmsg_add_str(m, "postproc", dvr_postproc);
|
||||
|
||||
|
|
|
@ -57,11 +57,9 @@ epg_set_current(channel_t *ch, event_t *e)
|
|||
return;
|
||||
|
||||
ch->ch_epg_current = e;
|
||||
if(e != NULL) {
|
||||
if(e != NULL)
|
||||
gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event,
|
||||
ch, MAX(e->e_stop, dispatch_clock + 1));
|
||||
dvr_autorec_check(e);
|
||||
}
|
||||
htsp_event_update(ch, e);
|
||||
}
|
||||
|
||||
|
@ -113,7 +111,7 @@ epg_ch_check_current_event(void *aux)
|
|||
static void
|
||||
epg_event_changed(event_t *e)
|
||||
{
|
||||
/* nothing atm */
|
||||
dvr_autorec_check_event(e);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -497,7 +497,10 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
return htsp_error("Event does not exist");
|
||||
|
||||
//create the dvr entry
|
||||
de = dvr_entry_create_by_event(e, (htsp->htsp_username) ? htsp->htsp_username:"anonymous");
|
||||
de = dvr_entry_create_by_event(e,
|
||||
htsp->htsp_username ?
|
||||
htsp->htsp_username : "anonymous",
|
||||
NULL);
|
||||
|
||||
dvr_status = de != NULL ? de->de_sched_state : DVR_NOSTATE;
|
||||
|
||||
|
|
|
@ -724,7 +724,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
|
|||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
dvr_entry_create_by_event(e, hc->hc_representative);
|
||||
dvr_entry_create_by_event(e, hc->hc_representative, NULL);
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
@ -777,7 +777,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
|
|||
if(stop < start)
|
||||
stop += 86400;
|
||||
|
||||
dvr_entry_create(ch, start, stop, title, NULL, hc->hc_representative);
|
||||
dvr_entry_create(ch, start, stop, title, NULL, hc->hc_representative, NULL);
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
|
|
@ -209,7 +209,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, hc->hc_username ?: "anonymous");
|
||||
de = dvr_entry_create_by_event(e, hc->hc_username ?: "anonymous", NULL);
|
||||
} else if(de != NULL && (http_arg_get(&hc->hc_req_args, "cancel")) != NULL) {
|
||||
de = dvr_entry_cancel(de);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue