diff --git a/debian/changelog b/debian/changelog index d74ef384..ce862f83 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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. diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 6bb0e254..f3699a0c 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -20,6 +20,7 @@ #define DVR_H #include +#include #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 */ diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 9a0baaec..1bf417bf 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -25,8 +25,6 @@ #include #include #include -#include - #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); + } } } diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index be73ada5..850bee6f 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -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); diff --git a/src/epg.c b/src/epg.c index 2598c8c9..2ecd38d7 100644 --- a/src/epg.c +++ b/src/epg.c @@ -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); } diff --git a/src/htsp.c b/src/htsp.c index 03f3ef41..0f55a541 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -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; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 20614e9a..a734122b 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -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); diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index ce45a624..4aeab012 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -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); }