From: E.Smith <31170571+azlm8t@users.noreply.github.com> Date: Tue, 12 Dec 2017 13:23:51 +0000 (+0000) Subject: dvr: Autorec rules must still match event after update. (#4760). X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=113dfd6b56ee2b485a142f70879a194ae4d99423;p=thirdparty%2Ftvheadend.git dvr: Autorec rules must still match event after update. (#4760). We now check the autorec rule matches an event following an update and on startup. When using xmltv grabbers you can have an autorec rule that matches "title: A". When a future xmltv run replaces the event with "title: B" then the dvr event is updated to reflect the new details (and will log updated title), but the autorec rule was not checked to see if it still matched the event. This meant we would record "title: B" even though the autorec was to only record "title A". Also, previously if you delete the epgdb between runs then the timers spawned by autorec rules remained. This could cause problems if the epgdb had been removed due to incorrect setup such as invalid xmltv files. This meant that you then had to manually disable and re-enable the autorec rules to remove these incorrect timers. Now we remove any record events created by an autorec rules if that event no longer matches the schedule. This does mean that if the user does _not_ persist the epgdb at all then, on startup, they no longer have autorecs timers matching until the epg is populated at startup, which is a change in behaviour (since previously autorec timers would remain) but could also be considered "expected" behaviour (no epgdb means no autorec timers). Issue: #4299. --- diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 222772128..73645efad 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -608,6 +608,7 @@ void dvr_event_removed(epg_broadcast_t *e); void dvr_event_updated(epg_broadcast_t *e); void dvr_event_running(epg_broadcast_t *e, epg_running_t running); +int dvr_entry_assign_broadcast(dvr_entry_t *de, epg_broadcast_t *bcast); dvr_entry_t *dvr_entry_find_by_id(int id); @@ -718,9 +719,16 @@ void dvr_autorec_init(void); void dvr_autorec_done(void); void dvr_autorec_update(void); -/// Check autorec timers after a short delay. +/* Check autorec timers after a short delay. */ void dvr_autorec_async_reschedule(void); +/* @return 1 if the event 'e' is matched by the autorec rule 'dae' */ +int dvr_autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e); +/* Purge any autorec timers that are obsolete since they no longer match any events. */ +void dvr_autorec_purge_obsolete_timers(void); +/* @return 1 if entry is an autorec and can be purged since it no longer matches its event. */ +int dvr_autorec_entry_can_be_purged(const dvr_entry_t *de); + static inline int dvr_autorec_entry_verify(dvr_autorec_entry_t *dae, access_t *a, int readonly) { diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index e9ae667bc..4a398791e 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -94,6 +94,60 @@ dvr_autorec_purge_spawns(dvr_autorec_entry_t *dae, int del, int disabled) return bcast; } + +/** + * If autorec entry no longer matches autorec rule then it can be purged. + * @return 1 if it can be purged + */ +int +dvr_autorec_entry_can_be_purged(const dvr_entry_t *de) +{ + /* Not an autorec so ignore */ + if (!de->de_autorec) + return 0; + + /* Entry is not scheduled (for example finished) so can not be purged */ + if (de->de_sched_state != DVR_SCHEDULED) + return 0; + + /* No broadcast matched when entry was reloaded from dvr/log */ + if (!de->de_bcast) + return 1; + + /* Confirm autorec still matches the broadcast */ + return !dvr_autorec_cmp(de->de_autorec, de->de_bcast); +} + +/** + * Purge any dvr_entry for autorec entries that + * no longer match the current schedule. + */ +void +dvr_autorec_purge_obsolete_timers(void) +{ + dvr_entry_t *de; + int num_purged = 0; + + LIST_FOREACH(de, &dvrentries, de_global_link) { + if (dvr_autorec_entry_can_be_purged(de)) { + char ubuf[UUID_HEX_SIZE]; + char t1buf[32], t2buf[32]; + tvhinfo(LS_DVR, "Entry %s can be purged for \"%s\" start %s stop %s", + idnode_uuid_as_str(&de->de_id, ubuf), + lang_str_get(de->de_title, NULL), + gmtime2local(de->de_start, t1buf, sizeof(t1buf)), + gmtime2local(de->de_stop, t2buf, sizeof(t2buf))); + + dvr_entry_assign_broadcast(de, NULL); + dvr_entry_destroy(de, 1); + ++num_purged; + } + } + if (num_purged) + tvhinfo(LS_DVR, "Purged %d autorec entries that no longer match schedule", num_purged); +} + + /** * Handle maxcount */ @@ -133,13 +187,14 @@ dvr_autorec_completed(dvr_autorec_entry_t *dae, int error_code) /** * return 1 if the event 'e' is matched by the autorec rule 'dae' */ -static int -autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e) +int +dvr_autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e) { idnode_list_mapping_t *ilm; dvr_config_t *cfg; double duration; + if (!e) return 0; if (!e->channel) return 0; if(dae->dae_enabled == 0 || dae->dae_weekdays == 0) return 0; @@ -1461,7 +1516,7 @@ dvr_autorec_check_event(epg_broadcast_t *e) if (e->channel && !e->channel->ch_enabled) return; TAILQ_FOREACH(dae, &autorec_entries, dae_link) - if(autorec_cmp(dae, e)) + if(dvr_autorec_cmp(dae, e)) dvr_entry_create_by_autorec(1, e, dae); // Note: no longer updating event here as it will be done from EPG // anyway @@ -1483,7 +1538,7 @@ dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge) CHANNEL_FOREACH(ch) { if (!ch->ch_enabled) continue; RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) { - if(autorec_cmp(dae, e)) { + if(dvr_autorec_cmp(dae, e)) { enabled = 1; if (disabled) { for (p = disabled; *p && *p != e; p++); diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 2d6dd0a76..8dea0aac6 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -237,7 +237,7 @@ dvr_entry_set_state(dvr_entry_t *de, dvr_entry_sched_state_t state, /* * */ -static int +int dvr_entry_assign_broadcast(dvr_entry_t *de, epg_broadcast_t *bcast) { char id[16]; @@ -2592,6 +2592,19 @@ dosave: updated ? " Timer" : "", dvr_updated_str(buf, sizeof(buf), save)); } + /* The updates could mean the autorec rule no longer matches the event, + * so we have to destroy the de and the conf and let the autorec rule + * re-create when it next matches. + * + * If de is not scheduled (i.e., recording) then do nothing with the + * rule since don't want to cancel items that are recording. + * Also if no event then this autorec can't match it so we should delete + * it. + */ + if (dvr_autorec_entry_can_be_purged(de)) { + dvr_entry_assign_broadcast(de, NULL); + dvr_entry_destroy(de, 1); + } } return de; @@ -4964,6 +4977,13 @@ dvr_entry_init(void) } htsmsg_destroy(l); } + /* We update the autorec entries so any that are no longer matching + * the current schedule get deleted. This avoids the problem where + * autorec entries remain even when user has deleted the epgdb + * or modified their settings between runs. + */ + tvhinfo(LS_DVR, "Purging obsolete autorec entries for current schedule"); + dvr_autorec_purge_obsolete_timers(); dvr_in_init = 0; /* process parent/child mapping */ HTSMSG_FOREACH(f, rere) {