19 #define RUNNINGSTATUSTIMEOUT 30
20 #define EPGDATAWRITEDELTA 600
33 unsigned int Stream, Type;
70 esyslog(
"ERROR: out of memory");
102 Stream == 2 && (
components[i].type < 5) == (Type < 5)
253 char vpsbuf[64] =
"";
284 switch (Content & 0xF0) {
286 switch (Content & 0x0F) {
288 case 0x00:
return tr(
"Content$Movie/Drama");
289 case 0x01:
return tr(
"Content$Detective/Thriller");
290 case 0x02:
return tr(
"Content$Adventure/Western/War");
291 case 0x03:
return tr(
"Content$Science Fiction/Fantasy/Horror");
292 case 0x04:
return tr(
"Content$Comedy");
293 case 0x05:
return tr(
"Content$Soap/Melodrama/Folkloric");
294 case 0x06:
return tr(
"Content$Romance");
295 case 0x07:
return tr(
"Content$Serious/Classical/Religious/Historical Movie/Drama");
296 case 0x08:
return tr(
"Content$Adult Movie/Drama");
300 switch (Content & 0x0F) {
302 case 0x00:
return tr(
"Content$News/Current Affairs");
303 case 0x01:
return tr(
"Content$News/Weather Report");
304 case 0x02:
return tr(
"Content$News Magazine");
305 case 0x03:
return tr(
"Content$Documentary");
306 case 0x04:
return tr(
"Content$Discussion/Interview/Debate");
310 switch (Content & 0x0F) {
312 case 0x00:
return tr(
"Content$Show/Game Show");
313 case 0x01:
return tr(
"Content$Game Show/Quiz/Contest");
314 case 0x02:
return tr(
"Content$Variety Show");
315 case 0x03:
return tr(
"Content$Talk Show");
319 switch (Content & 0x0F) {
321 case 0x00:
return tr(
"Content$Sports");
322 case 0x01:
return tr(
"Content$Special Event");
323 case 0x02:
return tr(
"Content$Sport Magazine");
324 case 0x03:
return tr(
"Content$Football/Soccer");
325 case 0x04:
return tr(
"Content$Tennis/Squash");
326 case 0x05:
return tr(
"Content$Team Sports");
327 case 0x06:
return tr(
"Content$Athletics");
328 case 0x07:
return tr(
"Content$Motor Sport");
329 case 0x08:
return tr(
"Content$Water Sport");
330 case 0x09:
return tr(
"Content$Winter Sports");
331 case 0x0A:
return tr(
"Content$Equestrian");
332 case 0x0B:
return tr(
"Content$Martial Sports");
336 switch (Content & 0x0F) {
338 case 0x00:
return tr(
"Content$Children's/Youth Programme");
339 case 0x01:
return tr(
"Content$Pre-school Children's Programme");
340 case 0x02:
return tr(
"Content$Entertainment Programme for 6 to 14");
341 case 0x03:
return tr(
"Content$Entertainment Programme for 10 to 16");
342 case 0x04:
return tr(
"Content$Informational/Educational/School Programme");
343 case 0x05:
return tr(
"Content$Cartoons/Puppets");
347 switch (Content & 0x0F) {
349 case 0x00:
return tr(
"Content$Music/Ballet/Dance");
350 case 0x01:
return tr(
"Content$Rock/Pop");
351 case 0x02:
return tr(
"Content$Serious/Classical Music");
352 case 0x03:
return tr(
"Content$Folk/Traditional Music");
353 case 0x04:
return tr(
"Content$Jazz");
354 case 0x05:
return tr(
"Content$Musical/Opera");
355 case 0x06:
return tr(
"Content$Ballet");
359 switch (Content & 0x0F) {
361 case 0x00:
return tr(
"Content$Arts/Culture");
362 case 0x01:
return tr(
"Content$Performing Arts");
363 case 0x02:
return tr(
"Content$Fine Arts");
364 case 0x03:
return tr(
"Content$Religion");
365 case 0x04:
return tr(
"Content$Popular Culture/Traditional Arts");
366 case 0x05:
return tr(
"Content$Literature");
367 case 0x06:
return tr(
"Content$Film/Cinema");
368 case 0x07:
return tr(
"Content$Experimental Film/Video");
369 case 0x08:
return tr(
"Content$Broadcasting/Press");
370 case 0x09:
return tr(
"Content$New Media");
371 case 0x0A:
return tr(
"Content$Arts/Culture Magazine");
372 case 0x0B:
return tr(
"Content$Fashion");
376 switch (Content & 0x0F) {
378 case 0x00:
return tr(
"Content$Social/Political/Economics");
379 case 0x01:
return tr(
"Content$Magazine/Report/Documentary");
380 case 0x02:
return tr(
"Content$Economics/Social Advisory");
381 case 0x03:
return tr(
"Content$Remarkable People");
385 switch (Content & 0x0F) {
387 case 0x00:
return tr(
"Content$Education/Science/Factual");
388 case 0x01:
return tr(
"Content$Nature/Animals/Environment");
389 case 0x02:
return tr(
"Content$Technology/Natural Sciences");
390 case 0x03:
return tr(
"Content$Medicine/Physiology/Psychology");
391 case 0x04:
return tr(
"Content$Foreign Countries/Expeditions");
392 case 0x05:
return tr(
"Content$Social/Spiritual Sciences");
393 case 0x06:
return tr(
"Content$Further Education");
394 case 0x07:
return tr(
"Content$Languages");
398 switch (Content & 0x0F) {
400 case 0x00:
return tr(
"Content$Leisure/Hobbies");
401 case 0x01:
return tr(
"Content$Tourism/Travel");
402 case 0x02:
return tr(
"Content$Handicraft");
403 case 0x03:
return tr(
"Content$Motoring");
404 case 0x04:
return tr(
"Content$Fitness & Health");
405 case 0x05:
return tr(
"Content$Cooking");
406 case 0x06:
return tr(
"Content$Advertisement/Shopping");
407 case 0x07:
return tr(
"Content$Gardening");
411 switch (Content & 0x0F) {
412 case 0x00:
return tr(
"Content$Original Language");
413 case 0x01:
return tr(
"Content$Black & White");
414 case 0x02:
return tr(
"Content$Unpublished");
415 case 0x03:
return tr(
"Content$Live Broadcast");
450 strftime(buf,
sizeof(buf),
"%d.%m. %R", localtime_r(&
vps, &tm_r));
459 fprintf(f,
"%sT %s\n", Prefix,
title);
461 fprintf(f,
"%sS %s\n", Prefix,
shortText);
468 fprintf(f,
"%sG", Prefix);
478 fprintf(f,
"%sX %s\n", Prefix, *p->
ToString());
482 fprintf(f,
"%sV %jd\n", Prefix, intmax_t(
vps));
485 fprintf(f,
"%s@ %s\n", Prefix,
aux);
489 fprintf(f,
"%se\n", Prefix);
508 int c = strtol(t, &tail, 16);
509 if (0x00 < c && c <= 0xFF) {
524 case 'V':
SetVps(atol(t));
529 default:
esyslog(
"ERROR: unexpected tag while reading EPG data: %s", s);
541 while ((s = ReadLine.
Read(f)) != NULL) {
545 case 'E':
if (!Event) {
552 if (n >= 3 && n <= 5) {
571 case 'e':
if (Event && !Event->
Title())
577 default:
if (Event && !Event->
Parse(s)) {
578 esyslog(
"ERROR: EPG data problem in line %d", Line);
583 esyslog(
"ERROR: unexpected end of file while reading EPG data");
588 #define MAXEPGBUGFIXSTATS 13
589 #define MAXEPGBUGFIXCHANS 100
605 for (; i < p->
n; i++) {
617 static time_t LastReport = 0;
618 time_t now = time(NULL);
619 if (now - LastReport > 3600 || Force) {
622 struct tm *ptm = localtime_r(&now, &tm_r);
623 if (ptm->tm_hour != 5)
628 bool GotHits =
false;
631 const char *delim =
" ";
634 bool PrintedStats =
false;
638 for (
int c = 0; c < p->
n; c++) {
641 dsyslog(
"=====================");
642 dsyslog(
"EPG bugfix statistics");
643 dsyslog(
"=====================");
644 dsyslog(
"IF SOMEBODY WHO IS IN CHARGE OF THE EPG DATA FOR ONE OF THE LISTED");
645 dsyslog(
"CHANNELS READS THIS: PLEASE TAKE A LOOK AT THE FUNCTION cEvent::FixEpgBugs()");
646 dsyslog(
"IN VDR/epg.c TO LEARN WHAT'S WRONG WITH YOUR DATA, AND FIX IT!");
647 dsyslog(
"=====================");
652 q += snprintf(q,
sizeof(buffer) - (q - buffer),
"%-3d %-4d", i, p->
hits);
655 q += snprintf(q,
sizeof(buffer) - (q - buffer),
"%s%s", delim, Channel->Name());
657 if (q - buffer > 80) {
658 q += snprintf(q,
sizeof(buffer) - (q - buffer),
"%s...", delim);
669 dsyslog(
"=====================");
680 if (l == 2 && *p == 0xC2)
682 if (*p == 0x86 || *p == 0x87 || *p == 0x0D) {
683 memmove(s, p + 1, len - l + 1);
716 const char *delim =
"\".";
717 char *e = strstr(p + 1, delim);
720 char *s = strdup(p + 1);
721 char *d = strdup(e + strlen(delim));
786 #define MAX_USEFUL_EPISODE_LENGTH 40
850 case 0x08: p->
description = strdup(
">16:9");
break;
852 case 0x0D: p->
description = strdup(
"HD 4:3");
break;
856 case 0x0F: p->
description = strdup(
"HD 16:9");
break;
858 case 0x10: p->
description = strdup(
"HD >16:9");
break;
877 case 0x05: p->
description = strdup(
"Dolby Digital");
break;
894 if (
char *p = strstr(
shortText,
"\\n")) {
1006 time_t now = time(NULL);
1008 if (p->StartTime() <= now)
1010 else if (p->StartTime() > now + 3600)
1024 time_t now = time(NULL);
1048 time_t delta = INT_MAX;
1051 if (dt >= 0 && dt < delta && p->EndTime() >= Time) {
1088 p->SetVersion(0xFF);
1100 if (SegmentStart > 0 && SegmentEnd > 0) {
1107 if ((p->
TableID() > 0x4E || TableID == 0x4E) && (p->
TableID() != TableID || p->
Version() != Version)) {
1123 if (n && n->
StartTime() == p->StartTime())
1155 fprintf(f,
"%sC %s %s\n", Prefix, *Channel->GetChannelID().ToString(), Channel->Name());
1178 default:
esyslog(
"ERROR: unknown DumpMode %d (%s %d)", DumpMode, __FUNCTION__, __LINE__);
1180 fprintf(f,
"%sc\n", Prefix);
1190 while ((s = ReadLine.
Read(f)) != NULL) {
1194 char *p = strchr(s,
' ');
1207 esyslog(
"ERROR: invalid channel ID: %s", s);
1213 esyslog(
"ERROR: unexpected tag in line %d while reading EPG data: %s", Line, s);
1229 virtual void Action(
void)
override;
1237 :
cThread(
"epg data writer", true)
1253 time_t now = time(NULL);
1254 for (
cSchedule *p = Schedules->First(); p; p = Schedules->
Next(p))
1297 time_t now = time(NULL);
1310 for (
cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->
Next(Schedule))
1311 Schedule->ResetVersions();
1329 for (
const cSchedule *p = Schedules->First(); p; p = Schedules->
Next(p))
1330 p->Dump(Channels, f, Prefix, DumpMode, AtTime);
1340 bool OwnFile = f == NULL;
1359 for (
cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1360 if (
const cSchedule *Schedule = Channel->schedule) {
1361 if (!Schedule->ChannelID().Valid())
1362 Channel->schedule = NULL;
1364 Schedules->GetSchedule(Channel);
1385 if (p->ChannelID() == ChannelID)
1401 Channel->
schedule = &DummySchedule;
1402 if (Channel->
schedule == &DummySchedule && AddIfMissing) {
1447 if (eh->IgnoreChannel(Channel))
1456 if (eh->HandleEitEvent(Schedule, EitEvent, TableID, Version))
1465 if (eh->HandledExternally(Channel))
1474 if (eh->IsUpdate(EventID, StartTime, TableID, Version))
1483 if (eh->SetEventID(Event, EventID))
1492 if (eh->SetTitle(Event, Title))
1501 if (eh->SetShortText(Event, ShortText))
1510 if (eh->SetDescription(Event, Description))
1519 if (eh->SetContents(Event, Contents))
1528 if (eh->SetParentalRating(Event, ParentalRating))
1537 if (eh->SetStartTime(Event, StartTime))
1546 if (eh->SetDuration(Event, Duration))
1555 if (eh->SetVps(Event, Vps))
1564 if (eh->SetComponents(Event, Components))
1573 if (eh->FixEpgBugs(Event))
1582 if (eh->HandleEvent(Event))
1590 if (eh->SortSchedule(Schedule))
1599 if (eh->DropOutdated(Schedule, SegmentStart, SegmentEnd, TableID, Version))
1602 Schedule->
DropOutdated(SegmentStart, SegmentEnd, TableID, Version);
1608 if (!eh->BeginSegmentTransfer(Channel,
false))
1617 if (eh->EndSegmentTransfer(Modified,
false))
#define LOCK_CHANNELS_READ
#define LOCK_CHANNELS_WRITE
const char * Name(void) const
tChannelID GetChannelID(void) const
const cSchedule * schedule
const cChannel * GetByChannelID(tChannelID ChannelID, bool TryWithoutRid=false, bool TryWithoutPolarization=false) const
tComponent * Component(int Index) const
tComponent * GetComponent(int Index, uchar Stream, uchar Type)
int NumComponents(void) const
void SetComponent(int Index, const char *s)
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
virtual ~cEpgHandler() override
cEpgHandler(void)
Constructs a new EPG handler and adds it to the list of EPG handlers.
void SortSchedule(cSchedule *Schedule)
void EndSegmentTransfer(bool Modified)
bool IgnoreChannel(const cChannel *Channel)
bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version)
void SetStartTime(cEvent *Event, time_t StartTime)
void SetTitle(cEvent *Event, const char *Title)
void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version)
void FixEpgBugs(cEvent *Event)
void HandleEvent(cEvent *Event)
void SetComponents(cEvent *Event, cComponents *Components)
void SetVps(cEvent *Event, time_t Vps)
void SetParentalRating(cEvent *Event, int ParentalRating)
bool BeginSegmentTransfer(const cChannel *Channel)
bool HandledExternally(const cChannel *Channel)
void SetContents(cEvent *Event, uchar *Contents)
void SetShortText(cEvent *Event, const char *ShortText)
void SetDuration(cEvent *Event, int Duration)
void SetDescription(cEvent *Event, const char *Description)
void SetEventID(cEvent *Event, tEventID EventID)
cString ToDescr(void) const
static const char * ContentToString(uchar Content)
uchar TableID(void) const
void SetAux(const char *Aux)
const char * Aux(void) const
time_t EndTime(void) const
static cMutex numTimersMutex
cString GetDateString(void) const
int RunningStatus(void) const
uchar Contents(int i=0) const
bool IsRunning(bool OrAboutToStart=false) const
void SetRunningStatus(int RunningStatus, const cChannel *Channel=NULL)
void IncNumTimers(void) const
int ParentalRating(void) const
time_t StartTime(void) const
tChannelID ChannelID(void) const
static bool Read(FILE *f, cSchedule *Schedule, int &Line)
void SetShortText(const char *ShortText)
cString GetTimeString(void) const
const cComponents * Components(void) const
void DecNumTimers(void) const
tEventID EventID(void) const
void SetStartTime(time_t StartTime)
bool HasTimer(void) const
const char * Title(void) const
void SetComponents(cComponents *Components)
void SetEventID(tEventID EventID)
const cSchedule * Schedule(void) const
cString GetEndTimeString(void) const
cString GetVpsString(void) const
void SetVersion(uchar Version)
void Dump(FILE *f, const char *Prefix="", bool InfoOnly=false) const
void SetDuration(int Duration)
void SetContents(uchar *Contents)
uchar Version(void) const
const char * ShortText(void) const
void SetTitle(const char *Title)
void SetTableID(uchar TableID)
uchar contents[MaxEventContents]
cString GetParentalRatingString(void) const
void SetDescription(const char *Description)
const char * Description(void) const
void SetParentalRating(int ParentalRating)
virtual int Compare(const cListObject &ListObject) const override
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
void Del(cListObject *Object, unsigned int Id)
void Add(cListObject *Object, unsigned int Id)
T * Get(unsigned int Id) const
void Del(cListObject *Object, bool DeleteObject=true)
void SetUseGarbageCollector(void)
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0) const
Tries to get a lock on this list and returns true if successful.
void Add(cListObject *Object, cListObject *After=NULL)
cListObject * Next(void) const
const T * Next(const T *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
const T * Prev(const T *Object) const
const cEvent * GetPresentEvent(void) const
cHash< cEvent > eventsHashID
bool HasTimer(void) const
void SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel=NULL)
void UnhashEvent(cEvent *Event)
const cEvent * GetEventAround(time_t Time) const
const cEvent * GetEventByTime(time_t StartTime) const
void DecNumTimers(void) const
bool OnActualTp(uchar TableId)
void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
static bool Read(FILE *f, cSchedules *Schedules)
cSchedule(tChannelID ChannelID)
void SetPresentSeen(void)
static cMutex numTimersMutex
void ClrRunningStatus(cChannel *Channel=NULL)
const cEvent * GetEventById(tEventID EventID) const
void DelEvent(cEvent *Event)
void HashEvent(cEvent *Event)
tChannelID ChannelID(void) const
cHash< cEvent > eventsHashStartTime
void IncNumTimers(void) const
cEvent * AddEvent(cEvent *Event)
void Dump(const cChannels *Channels, FILE *f, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0) const
const cEvent * GetFollowingEvent(void) const
static cSchedules * GetSchedulesWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of schedules for write access.
const cSchedule * GetSchedule(tChannelID ChannelID) const
static const cSchedules * GetSchedulesRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of schedules for read access.
static bool Dump(FILE *f=NULL, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0)
static void SetEpgDataFileName(const char *FileName)
static void Cleanup(bool Force=false)
static char * epgDataFileName
static void ResetVersions(void)
cSchedule * AddSchedule(tChannelID ChannelID)
static bool Read(FILE *f=NULL)
static cSchedules schedules
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
static cString sprintf(const char *fmt,...) __attribute__((format(printf
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
bool Active(void)
Checks whether the thread is still alive.
#define EPGDATAWRITEDELTA
static void StripControlCharacters(char *s)
tEpgBugFixStats EpgBugFixStats[MAXEPGBUGFIXSTATS]
void ReportEpgBugFixStats(bool Force)
#define MAXEPGBUGFIXCHANS
#define MAX_USEFUL_EPISODE_LENGTH
#define RUNNINGSTATUSTIMEOUT
#define MAXEPGBUGFIXSTATS
static void EpgBugFixStat(int Number, tChannelID ChannelID)
static cEpgDataWriter EpgDataWriter
#define LOCK_SCHEDULES_READ
#define LOCK_SCHEDULES_WRITE
@ ecgSocialPoliticalEconomics
static int Utf8CharLen(const char *s)
@ RunningStatusNotRunning
@ RunningStatusStartsInAFewSeconds
tChannelID & ClrRid(void)
static const tChannelID InvalidID
static tChannelID FromString(const char *s)
bool FromString(const char *s)
char language[MAXLANGCODE2]
tChannelID channelIDs[MAXEPGBUGFIXCHANS]