diff options
-rw-r--r-- | HISTORY | 7 | ||||
-rw-r--r-- | README | 27 | ||||
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | vdrtva.c | 192 | ||||
-rw-r--r-- | vdrtva.h | 26 |
5 files changed, 148 insertions, 108 deletions
@@ -50,3 +50,10 @@ VDR Plugin 'vdrtva' Revision History - Start capturing CRID data soon after VDR startup. - COPYING file omitted from distribution. - Update time wrong after DST change. + +2012-04-20: Version 0.2.0 +- Improvements suggested by Richard Scobie: + - Ignore repeating timers. + - Handle timers recording into subdirectories. + - Gather CRID data continuously. +- Plugin now compatible with New Zealand DVB-S. @@ -16,7 +16,9 @@ standard is ETSI TS 102 323. In the UK a subset of the TV-Anytime specification is broadcast on the DTV service under the trade name "FreeView Plus". This plugin is written for the UK -version but should work with the full specification (untested). +version, has been tested on New Zealand DVB-S and is believed to be compatible +with the NorDig standard used in Scandanavian countries plus Eire. It should +also work with the full specification (untested). TV-Anytime data is contained in Content Reference Identifiers (CRIDs). The syntax of a CRID is described in RFC 4078; it is a URI-compliant string of the @@ -53,8 +55,9 @@ Operation: The use of the 'Accurate Recording' feature is described in README-vps. -The plugin runs every 24 hours at a time set by the '-u' parameter or in VDR's -OSD (default 03:00). It captures CRID data for a time (default 10 minutes) then: +The plugin captures CRID data continuously, beginning 5 minutes after VDR +startup. A maintenance task runs every 24 hours at a time set by the '-u' +parameter or in VDR's OSD (default 03:00) which: - Checks for new manually-created timers and adds series links for them. @@ -66,9 +69,13 @@ OSD (default 03:00). It captures CRID data for a time (default 10 minutes) then: - Checks that the event being recorded by each timer is the same as when the timer was set (ie that the EPG has not changed in the meantime) -- Flags any split events (eg a long programme with a news summary in the middle). - At present a manual check is needed that all parts of the programme are set to - be recorded. +- Flags any split events (eg a long programme with a news summary in the + middle). At present a manual check is needed that all parts of the programme + are set to be recorded. + +- Optionally sends a mail report listing new timers which have been + automatically created, any timer clash warnings, and a complete list of timers + and any suggestions for them. The plugin takes the following parameters: @@ -121,8 +128,8 @@ Points to remember: - The 'suggested' events list may have CRIDs which do not appear in the events list. -The plugin is compatible with VDR version >= 1.7.19. The patch version will work -with VDR 1.6. +The plugin is compatible with VDR version >= 1.7.19. The patch version will +work with VDR 1.6. -Although I use this software on my VDR installation, this is Beta-quality code -- USE AT YOUR OWN RISK!! +Although I use this software day-to-day on my VDR installation, this is +Beta-quality code - USE AT YOUR OWN RISK!! @@ -10,5 +10,7 @@ Some events have a series CRID but no item CRID - how to handle these? Delete a series link if the only timer is manually deleted. +Display suggestions for timers which don't have series CRIDs. + Bugs: - Very rare crash 'pure virtual method called' in plugin. + Very rare crash 'pure virtual method called' in plugin - possibly solved. @@ -22,7 +22,7 @@ cEventCRIDs *EventCRIDs; cSuggestCRIDs *SuggestCRIDs; cLinks *Links; -static const char *VERSION = "0.1.3"; +static const char *VERSION = "0.2.0"; static const char *DESCRIPTION = "Series Record plugin"; //static const char *MAINMENUENTRY = "vdrTva"; @@ -58,6 +58,7 @@ private: void Update(void); void Check(void); void Report(void); + void Expire(void); void tvasyslog(const char *Fmt, ...); time_t NextUpdateTime(void); @@ -229,18 +230,15 @@ void cPluginvdrTva::Housekeeping(void) state++; break; case 1: - StopDataCapture(); - state++; - break; - case 2: + Expire(); Update(); state++; break; - case 3: + case 2: Check(); Report(); nextactiontime = NextUpdateTime(); - state = 0; + state = 1; tvalog.MailLog(); break; } @@ -463,24 +461,22 @@ void cPluginvdrTva::StartDataCapture() void cPluginvdrTva::StopDataCapture() { if (Filter) { - cDevice::ActualDevice()->Detach(Filter); delete Filter; Filter = NULL; - if (SuggestCRIDs && (SuggestCRIDs->MaxNumber() >= 1)) { // De-dup the suggestions list. - SuggestCRIDs->Sort(); - cSuggestCRID *suggest = SuggestCRIDs->First(); - while (suggest) { - cSuggestCRID *next = SuggestCRIDs->Next(suggest); - if (next && !strcmp(next->iCRID(), suggest->iCRID()) && !strcmp(next->gCRID(), suggest->gCRID())) { - SuggestCRIDs->Del(suggest); - } - suggest = next; - } - } isyslog("vdrtva: Data capture stopped"); } } +void cPluginvdrTva::Expire() +{ + if (!EventCRIDs) return; + EventCRIDs->Expire(); + if (SuggestCRIDs) { + SuggestCRIDs->DeDup(); + SuggestCRIDs->Expire(); + } +} + void cPluginvdrTva::Update() { bool status = UpdateLinksFromTimers(); @@ -1103,11 +1099,11 @@ void cTvaFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length sectionSyncer.Reset(); SI::SDT sdt(Data, false); if (!sdt.CheckCRCAndParse()) { - dsyslog ("vdrtva: SDT Parse / CRC error\n"); + dsyslog ("vdrtva: SDT Parse / CRC error"); return; } if (!sectionSyncer.Sync(sdt.getVersionNumber(), sdt.getSectionNumber(), sdt.getLastSectionNumber())) { - dsyslog ("vdrtva: SDT Syncer error\n"); + dsyslog ("vdrtva: SDT Syncer error"); return; } SI::SDT::Service SiSdtService; @@ -1123,8 +1119,7 @@ void cTvaFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length SI::DefaultAuthorityDescriptor *da = (SI::DefaultAuthorityDescriptor *)d; char DaBuf[Utf8BufSize(1024)]; da->DefaultAuthority.getText(DaBuf, sizeof(DaBuf)); - chanDA = ChanDAs->NewChanDA(chan->Number()); - chanDA->SetDA(DaBuf); + chanDA = ChanDAs->NewChanDA(chan->Number(), DaBuf); } break; default: ; @@ -1140,7 +1135,7 @@ void cTvaFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length // sectionSyncer.Reset(); SI::EIT eit(Data, false); if (!eit.CheckCRCAndParse()) { - dsyslog ("vdrtva: EIT Parse / CRC error\n"); + dsyslog ("vdrtva: EIT Parse / CRC error"); return; } @@ -1175,12 +1170,12 @@ void cTvaFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length // There can be more than one type 0x33 descriptor per event (each with one CRID). case 0x03: case 0x33: - cde.identifier.getText(gCRIDBuf, sizeof(gCRIDBuf)); // FIXME Rashly assuming that a 0x31 CRID will always precede a 0x33 CRID. - if (iCRIDBuf[0]) SuggestCRIDs->NewSuggestCRID(chan->Number(), iCRIDBuf, gCRIDBuf); + cde.identifier.getText(gCRIDBuf, sizeof(gCRIDBuf)); // FIXME Rashly assuming that 0x31 & 0x32 CRIDs will always precede a 0x33 CRID. + if (iCRIDBuf[0] && sCRIDBuf[0]) SuggestCRIDs->NewSuggestCRID(chan->Number(), iCRIDBuf, gCRIDBuf); } } else { - dsyslog ("vdrtva: Incorrect CRID Loc %x\n", cde.getCridLocation()); + dsyslog ("vdrtva: Incorrect CRID Loc %x", cde.getCridLocation()); } } } @@ -1190,8 +1185,7 @@ void cTvaFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length delete d; } if (iCRIDBuf[0] && sCRIDBuf[0]) { // Only log events which are part of a series. - eventCRID = EventCRIDs->NewEventCRID(chan->Number(), SiEitEvent.getEventId()); - eventCRID->SetCRIDs(iCRIDBuf, sCRIDBuf); + eventCRID = EventCRIDs->NewEventCRID(chan->Number(), SiEitEvent.getEventId(), iCRIDBuf, sCRIDBuf); } } } @@ -1206,9 +1200,10 @@ void cTvaFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length cChanDA - Default Authority for a channel. */ -cChanDA::cChanDA(void) +cChanDA::cChanDA(int Cid, char *DA) { - defaultAuthority = NULL; + cid = Cid; + defaultAuthority = strcpyrealloc(NULL, DA); } cChanDA::~cChanDA(void) @@ -1216,15 +1211,6 @@ cChanDA::~cChanDA(void) free(defaultAuthority); } - -void cChanDA::Set(int Cid) { - cid = Cid; -} - -void cChanDA::SetDA(char *DA) { - defaultAuthority = strcpyrealloc(defaultAuthority, DA); -} - /* cChanDAs - in-memory list of channels and Default Authorities. */ @@ -1252,13 +1238,12 @@ cChanDA *cChanDAs::GetByChannelID(int cid) return NULL; } -cChanDA *cChanDAs::NewChanDA(int Cid) +cChanDA *cChanDAs::NewChanDA(int Cid, char *DA) { - cChanDA *NewChanDA = new cChanDA; - NewChanDA->Set(Cid); + cChanDA *NewChanDA = new cChanDA(Cid, DA); Add(NewChanDA); chanDAHash.Add(NewChanDA, Cid); - ChanDAs->SetMaxNumber(ChanDAs->MaxNumber()+1); + maxNumber++; return NewChanDA; } @@ -1267,9 +1252,12 @@ cChanDA *cChanDAs::NewChanDA(int Cid) cEventCRID - CRIDs for an event. */ -cEventCRID::cEventCRID(void) +cEventCRID::cEventCRID(int Cid, tEventID Eid, char *iCRID, char *sCRID) { - iCrid = sCrid = NULL; + eid = Eid; + cid = Cid; + iCrid = strcpyrealloc(NULL, iCRID); + sCrid = strcpyrealloc(NULL, sCRID); } cEventCRID::~cEventCRID(void) @@ -1278,16 +1266,6 @@ cEventCRID::~cEventCRID(void) free (sCrid); } -void cEventCRID::Set(int Cid, tEventID Eid) { - eid = Eid; - cid = Cid; -} - -void cEventCRID::SetCRIDs(char *iCRID, char *sCRID) { - iCrid = strcpyrealloc(iCrid, iCRID); - sCrid = strcpyrealloc(sCrid, sCRID); -} - /* cEventCRIDs - in-memory list of events and CRIDs. @@ -1316,24 +1294,49 @@ cEventCRID *cEventCRIDs::GetByID(int Cid, tEventID Eid) return NULL; } -cEventCRID *cEventCRIDs::NewEventCRID(int Cid, tEventID Eid) +cEventCRID *cEventCRIDs::NewEventCRID(int Cid, tEventID Eid, char *iCRID, char *sCRID) { - cEventCRID *NewEventCRID = new cEventCRID; - NewEventCRID->Set(Cid, Eid); + cEventCRID *NewEventCRID = new cEventCRID(Cid, Eid, iCRID, sCRID); Add(NewEventCRID); EventCRIDHash.Add(NewEventCRID, Eid + Cid*33000); - EventCRIDs->SetMaxNumber(EventCRIDs->MaxNumber()+1); + maxNumber++; return NewEventCRID; } +void cEventCRIDs::Expire(void) +{ + int i = 0; + cSchedulesLock SchedulesLock; + const cSchedules *schedules = cSchedules::Schedules(SchedulesLock); + if (schedules) { + cEventCRID *crid = First(); + while (crid) { + cEventCRID *next = Next(crid); + cChannel *channel = Channels.GetByNumber(crid->Cid()); + const cSchedule *schedule = schedules->GetSchedule(channel); + if (schedule) { + const cEvent *event = schedule->GetEvent(crid->Eid(), 0); + if (!event) { + Del(crid); + i++; + } + } + crid = next; + } + } + dsyslog("vdrtva: %d expired CRIDs removed", i); +} + /* cSuggestCRID - CRIDs of suggested items for an event. */ -cSuggestCRID::cSuggestCRID(void) +cSuggestCRID::cSuggestCRID(int Cid, char *iCRID, char *gCRID) { - iCrid = gCrid = NULL; + iCrid = strcpyrealloc(NULL, iCRID); + gCrid = strcpyrealloc(NULL, gCRID); + cid = Cid; } cSuggestCRID::~cSuggestCRID(void) @@ -1342,12 +1345,6 @@ cSuggestCRID::~cSuggestCRID(void) free (gCrid); } -void cSuggestCRID::Set(int Cid, char *iCRID, char *gCRID) { - iCrid = strcpyrealloc(iCrid, iCRID); - gCrid = strcpyrealloc(gCrid, gCRID); - cid = Cid; -} - int cSuggestCRID::Compare(const cListObject &ListObject) const { cSuggestCRID *s = (cSuggestCRID *) &ListObject; @@ -1367,27 +1364,63 @@ cSuggestCRIDs::cSuggestCRIDs(void) maxNumber = 0; } -cSuggestCRIDs::~cSuggestCRIDs(void) -{ -} - cSuggestCRID *cSuggestCRIDs::NewSuggestCRID(int cid, char *icrid, char *gcrid) { - cSuggestCRID *NewSuggestCRID = new cSuggestCRID; - NewSuggestCRID->Set(cid, icrid, gcrid); + cSuggestCRID *NewSuggestCRID = new cSuggestCRID(cid, icrid, gcrid); Add(NewSuggestCRID); - SuggestCRIDs->SetMaxNumber(SuggestCRIDs->MaxNumber()+1); + maxNumber++; return NewSuggestCRID; } +void cSuggestCRIDs::DeDup(void) { + if (maxNumber < 2) return; + int i = 0; + Sort(); + cSuggestCRID *suggest = First(); + while (suggest) { + cSuggestCRID *next = Next(suggest); + if (next && !strcmp(next->iCRID(), suggest->iCRID()) && !strcmp(next->gCRID(), suggest->gCRID())) { + Del(suggest); + i++; + } + suggest = next; + } + dsyslog("vdrtva: %d duplicate suggestions removed", i); +} + +void cSuggestCRIDs::Expire(void) { + if (maxNumber == 0) return; + int i = 0; + cSuggestCRID *suggest = First(); + while (suggest) { + cSuggestCRID *next = Next(suggest); + bool found = false; + for (cEventCRID *crid = EventCRIDs->First(); crid; crid = EventCRIDs->Next(crid)) { + if (!strcmp(suggest->iCRID(), crid->iCRID())) { + found = true; + break; + } + } + if (!found) { + Del(suggest); + i++; + } + suggest = next; + } + dsyslog("vdrtva: %d expired suggestions removed", i); +} + /* cLinkItem - Entry from the links file */ -cLinkItem::cLinkItem(void) +cLinkItem::cLinkItem(const char *sCRID, int ModTime, const char *iCRIDs, const char *Path) { - sCrid = iCrids = path = NULL; + sCrid = strcpyrealloc(NULL, sCRID); + modtime = ModTime; + iCrids = strcpyrealloc(NULL, iCRIDs); + path = strcpyrealloc(NULL, Path); } cLinkItem::~cLinkItem(void) @@ -1416,10 +1449,9 @@ cLinks::cLinks(void) cLinkItem *cLinks::NewLinkItem(const char *sCRID, int ModTime, const char *iCRIDs, const char *path) { - cLinkItem *NewLinkItem = new cLinkItem; - NewLinkItem->Set(sCRID, ModTime, iCRIDs, path); + cLinkItem *NewLinkItem = new cLinkItem(sCRID, ModTime, iCRIDs, path); Add(NewLinkItem); - Links->SetMaxNumber(Links->MaxNumber()+1); + maxNumber++; return NewLinkItem; } @@ -86,12 +86,10 @@ class cChanDA : public cListObject { int cid; char *defaultAuthority; public: - cChanDA(void); + cChanDA(int Cid, char *DA); ~cChanDA(void); int Cid(void) { return cid; } - void Set(int Cid); char * DA(void) { return defaultAuthority; } - void SetDA(char *DA); }; class cChanDAs : public cRwLock, public cConfig<cChanDA> { @@ -102,9 +100,8 @@ class cChanDAs : public cRwLock, public cConfig<cChanDA> { cChanDAs(void); ~cChanDAs(void); int MaxNumber(void) { return maxNumber; } - void SetMaxNumber(int number) { maxNumber = number; } cChanDA *GetByChannelID(int cid); - cChanDA *NewChanDA(int Cid); + cChanDA *NewChanDA(int Cid, char *DA); }; @@ -115,13 +112,11 @@ class cEventCRID : public cListObject { char *iCrid; char *sCrid; public: - cEventCRID(void); + cEventCRID(int Cid, tEventID Eid, char *iCRID, char *sCRID); ~cEventCRID(void); tEventID Eid(void) { return eid; } - void Set(int Cid, tEventID Eid); char * iCRID(void) { return iCrid; } char * sCRID(void) { return sCrid; } - void SetCRIDs(char *iCRID, char *sCRID); int Cid(void) { return cid; } }; @@ -133,9 +128,9 @@ class cEventCRIDs : public cRwLock, public cConfig<cEventCRID> { cEventCRIDs(void); ~cEventCRIDs(void); int MaxNumber(void) { return maxNumber; } - void SetMaxNumber(int number) { maxNumber = number; } cEventCRID *GetByID(int Cid, tEventID Eid); - cEventCRID *NewEventCRID(int Cid, tEventID Eid); + cEventCRID *NewEventCRID(int Cid, tEventID Eid, char *iCRID, char *sCRID); + void Expire(void); }; @@ -145,12 +140,11 @@ class cSuggestCRID : public cListObject { char *gCrid; int cid; public: - cSuggestCRID(void); + cSuggestCRID(int Cid, char *iCRID, char *gCRID); ~cSuggestCRID(void); char * iCRID(void) { return iCrid; } char * gCRID(void) { return gCrid; } int Cid(void) { return cid; } - void Set(int Cid, char *iCRID, char *gCRID); virtual int Compare(const cListObject &ListObject) const; }; @@ -160,10 +154,10 @@ class cSuggestCRIDs : public cRwLock, public cConfig<cSuggestCRID> { int maxNumber; public: cSuggestCRIDs(void); - ~cSuggestCRIDs(void); int MaxNumber(void) { return maxNumber; } - void SetMaxNumber(int number) { maxNumber = number; } cSuggestCRID *NewSuggestCRID(int Cid, char *icrid, char *gcrid); + void DeDup(void); + void Expire(void); }; @@ -174,7 +168,7 @@ class cLinkItem : public cListObject { char *iCrids; char *path; public: - cLinkItem(void); + cLinkItem(const char *sCRID, int ModTime, const char *iCRIDs, const char *Path); ~cLinkItem(void); void Set(const char *sCRID, int ModTime, const char *iCRIDs, const char *Path); char * iCRIDs(void) { return iCrids; } @@ -188,8 +182,6 @@ class cLinks : public cRwLock, public cConfig<cLinkItem> { int maxNumber; public: cLinks(void); -// ~cLinks(void); int MaxNumber(void) { return maxNumber; } - void SetMaxNumber(int number) { maxNumber = number; } cLinkItem *NewLinkItem(const char *sCRID, int ModTime, const char *iCRIDs, const char *Path); }; |