vdr  2.6.1
epg.c
Go to the documentation of this file.
1 /*
2  * epg.c: Electronic Program Guide
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * Original version (as used in VDR before 1.3.0) written by
8  * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
9  *
10  * $Id: epg.c 5.6 2021/04/29 09:19:58 kls Exp $
11  */
12 
13 #include "epg.h"
14 #include <ctype.h>
15 #include <limits.h>
16 #include <time.h>
17 #include "libsi/si.h"
18 
19 #define RUNNINGSTATUSTIMEOUT 30 // seconds before the running status is considered unknown
20 #define EPGDATAWRITEDELTA 600 // seconds between writing the epg.data file
21 
22 // --- tComponent ------------------------------------------------------------
23 
25 {
26  char buffer[256];
27  snprintf(buffer, sizeof(buffer), "%X %02X %s %s", stream, type, language, description ? description : "");
28  return buffer;
29 }
30 
31 bool tComponent::FromString(const char *s)
32 {
33  unsigned int Stream, Type;
34  int n = sscanf(s, "%X %02X %7s %m[^\n]", &Stream, &Type, language, &description); // 7 = MAXLANGCODE2 - 1
35  if (n != 4 || isempty(description)) {
36  free(description);
37  description = NULL;
38  }
39  stream = Stream;
40  type = Type;
41  return n >= 3;
42 }
43 
44 // --- cComponents -----------------------------------------------------------
45 
47 {
48  numComponents = 0;
49  components = NULL;
50 }
51 
53 {
54  for (int i = 0; i < numComponents; i++)
55  free(components[i].description);
56  free(components);
57 }
58 
59 bool cComponents::Realloc(int Index)
60 {
61  if (Index >= numComponents) {
62  Index++;
63  if (tComponent *NewBuffer = (tComponent *)realloc(components, Index * sizeof(tComponent))) {
64  int n = numComponents;
65  numComponents = Index;
66  components = NewBuffer;
67  memset(&components[n], 0, sizeof(tComponent) * (numComponents - n));
68  }
69  else {
70  esyslog("ERROR: out of memory");
71  return false;
72  }
73  }
74  return true;
75 }
76 
77 void cComponents::SetComponent(int Index, const char *s)
78 {
79  if (Realloc(Index))
80  components[Index].FromString(s);
81 }
82 
83 void cComponents::SetComponent(int Index, uchar Stream, uchar Type, const char *Language, const char *Description)
84 {
85  if (!Realloc(Index))
86  return;
87  tComponent *p = &components[Index];
88  p->stream = Stream;
89  p->type = Type;
90  strn0cpy(p->language, Language, sizeof(p->language));
91  char *q = strchr(p->language, ',');
92  if (q)
93  *q = 0; // strips rest of "normalized" language codes
94  p->description = strcpyrealloc(p->description, !isempty(Description) ? Description : NULL);
95 }
96 
98 {
99  for (int i = 0; i < numComponents; i++) {
100  if (components[i].stream == Stream && (
101  Type == 0 || // don't care about the actual Type
102  Stream == 2 && (components[i].type < 5) == (Type < 5) // fallback "Dolby" component according to the "Premiere pseudo standard"
103  )) {
104  if (!Index--)
105  return &components[i];
106  }
107  }
108  return NULL;
109 }
110 
111 // --- cEvent ----------------------------------------------------------------
112 
114 
116 {
117  schedule = NULL;
118  numTimers = 0;
119  eventID = EventID;
120  tableID = 0xFF; // actual table ids are 0x4E..0x60
121  version = 0xFF; // actual version numbers are 0..31
123  title = NULL;
124  shortText = NULL;
125  description = NULL;
126  components = NULL;
127  memset(contents, 0, sizeof(contents));
128  parentalRating = 0;
129  startTime = 0;
130  duration = 0;
131  vps = 0;
132  aux = NULL;
133  SetSeen();
134 }
135 
137 {
138  free(title);
139  free(shortText);
140  free(description);
141  free(aux);
142  delete components;
143 }
144 
145 int cEvent::Compare(const cListObject &ListObject) const
146 {
147  cEvent *e = (cEvent *)&ListObject;
148  return startTime - e->startTime;
149 }
150 
152 {
153  return schedule ? schedule->ChannelID() : tChannelID();
154 }
155 
157 {
158  if (eventID != EventID) {
159  if (schedule)
160  schedule->UnhashEvent(this);
161  eventID = EventID;
162  if (schedule)
163  schedule->HashEvent(this);
164  }
165 }
166 
168 {
169  tableID = TableID;
170 }
171 
173 {
174  version = Version;
175 }
176 
178 {
180  isyslog("channel %d (%s) event %s status %d", Channel->Number(), Channel->Name(), *ToDescr(), RunningStatus);
182 }
183 
184 void cEvent::SetTitle(const char *Title)
185 {
187 }
188 
189 void cEvent::SetShortText(const char *ShortText)
190 {
192 }
193 
194 void cEvent::SetDescription(const char *Description)
195 {
197 }
198 
200 {
201  delete components;
203 }
204 
205 void cEvent::SetContents(uchar *Contents)
206 {
207  for (int i = 0; i < MaxEventContents; i++)
208  contents[i] = Contents[i];
209 }
210 
211 void cEvent::SetParentalRating(int ParentalRating)
212 {
214 }
215 
216 void cEvent::SetStartTime(time_t StartTime)
217 {
218  if (startTime != StartTime) {
219  if (schedule)
220  schedule->UnhashEvent(this);
222  if (schedule)
223  schedule->HashEvent(this);
224  }
225 }
226 
227 void cEvent::SetDuration(int Duration)
228 {
229  duration = Duration;
230 }
231 
232 void cEvent::SetVps(time_t Vps)
233 {
234  vps = Vps;
235 }
236 
237 void cEvent::SetSeen(void)
238 {
239  seen = time(NULL);
240 }
241 
242 void cEvent::SetAux(const char *Aux)
243 {
244  free(aux);
245  aux = Aux ? strdup(Aux) : NULL;
246 }
247 
249 {
250  char vpsbuf[64] = "";
251  if (Vps())
252  sprintf(vpsbuf, "(VPS: %s) ", *GetVpsString());
253  return cString::sprintf("%s %s-%s %s'%s'", *GetDateString(), *GetTimeString(), *GetEndTimeString(), vpsbuf, Title());
254 }
255 
256 void cEvent::IncNumTimers(void) const
257 {
259  numTimers++;
260  if (schedule)
263 }
264 
265 void cEvent::DecNumTimers(void) const
266 {
268  numTimers--;
269  if (schedule)
272 }
273 
274 bool cEvent::IsRunning(bool OrAboutToStart) const
275 {
277 }
278 
279 const char *cEvent::ContentToString(uchar Content)
280 {
281  switch (Content & 0xF0) {
282  case ecgMovieDrama:
283  switch (Content & 0x0F) {
284  default:
285  case 0x00: return tr("Content$Movie/Drama");
286  case 0x01: return tr("Content$Detective/Thriller");
287  case 0x02: return tr("Content$Adventure/Western/War");
288  case 0x03: return tr("Content$Science Fiction/Fantasy/Horror");
289  case 0x04: return tr("Content$Comedy");
290  case 0x05: return tr("Content$Soap/Melodrama/Folkloric");
291  case 0x06: return tr("Content$Romance");
292  case 0x07: return tr("Content$Serious/Classical/Religious/Historical Movie/Drama");
293  case 0x08: return tr("Content$Adult Movie/Drama");
294  }
295  break;
297  switch (Content & 0x0F) {
298  default:
299  case 0x00: return tr("Content$News/Current Affairs");
300  case 0x01: return tr("Content$News/Weather Report");
301  case 0x02: return tr("Content$News Magazine");
302  case 0x03: return tr("Content$Documentary");
303  case 0x04: return tr("Content$Discussion/Inverview/Debate");
304  }
305  break;
306  case ecgShow:
307  switch (Content & 0x0F) {
308  default:
309  case 0x00: return tr("Content$Show/Game Show");
310  case 0x01: return tr("Content$Game Show/Quiz/Contest");
311  case 0x02: return tr("Content$Variety Show");
312  case 0x03: return tr("Content$Talk Show");
313  }
314  break;
315  case ecgSports:
316  switch (Content & 0x0F) {
317  default:
318  case 0x00: return tr("Content$Sports");
319  case 0x01: return tr("Content$Special Event");
320  case 0x02: return tr("Content$Sport Magazine");
321  case 0x03: return tr("Content$Football/Soccer");
322  case 0x04: return tr("Content$Tennis/Squash");
323  case 0x05: return tr("Content$Team Sports");
324  case 0x06: return tr("Content$Athletics");
325  case 0x07: return tr("Content$Motor Sport");
326  case 0x08: return tr("Content$Water Sport");
327  case 0x09: return tr("Content$Winter Sports");
328  case 0x0A: return tr("Content$Equestrian");
329  case 0x0B: return tr("Content$Martial Sports");
330  }
331  break;
332  case ecgChildrenYouth:
333  switch (Content & 0x0F) {
334  default:
335  case 0x00: return tr("Content$Children's/Youth Programme");
336  case 0x01: return tr("Content$Pre-school Children's Programme");
337  case 0x02: return tr("Content$Entertainment Programme for 6 to 14");
338  case 0x03: return tr("Content$Entertainment Programme for 10 to 16");
339  case 0x04: return tr("Content$Informational/Educational/School Programme");
340  case 0x05: return tr("Content$Cartoons/Puppets");
341  }
342  break;
343  case ecgMusicBalletDance:
344  switch (Content & 0x0F) {
345  default:
346  case 0x00: return tr("Content$Music/Ballet/Dance");
347  case 0x01: return tr("Content$Rock/Pop");
348  case 0x02: return tr("Content$Serious/Classical Music");
349  case 0x03: return tr("Content$Folk/Tradional Music");
350  case 0x04: return tr("Content$Jazz");
351  case 0x05: return tr("Content$Musical/Opera");
352  case 0x06: return tr("Content$Ballet");
353  }
354  break;
355  case ecgArtsCulture:
356  switch (Content & 0x0F) {
357  default:
358  case 0x00: return tr("Content$Arts/Culture");
359  case 0x01: return tr("Content$Performing Arts");
360  case 0x02: return tr("Content$Fine Arts");
361  case 0x03: return tr("Content$Religion");
362  case 0x04: return tr("Content$Popular Culture/Traditional Arts");
363  case 0x05: return tr("Content$Literature");
364  case 0x06: return tr("Content$Film/Cinema");
365  case 0x07: return tr("Content$Experimental Film/Video");
366  case 0x08: return tr("Content$Broadcasting/Press");
367  case 0x09: return tr("Content$New Media");
368  case 0x0A: return tr("Content$Arts/Culture Magazine");
369  case 0x0B: return tr("Content$Fashion");
370  }
371  break;
373  switch (Content & 0x0F) {
374  default:
375  case 0x00: return tr("Content$Social/Political/Economics");
376  case 0x01: return tr("Content$Magazine/Report/Documentary");
377  case 0x02: return tr("Content$Economics/Social Advisory");
378  case 0x03: return tr("Content$Remarkable People");
379  }
380  break;
382  switch (Content & 0x0F) {
383  default:
384  case 0x00: return tr("Content$Education/Science/Factual");
385  case 0x01: return tr("Content$Nature/Animals/Environment");
386  case 0x02: return tr("Content$Technology/Natural Sciences");
387  case 0x03: return tr("Content$Medicine/Physiology/Psychology");
388  case 0x04: return tr("Content$Foreign Countries/Expeditions");
389  case 0x05: return tr("Content$Social/Spiritual Sciences");
390  case 0x06: return tr("Content$Further Education");
391  case 0x07: return tr("Content$Languages");
392  }
393  break;
394  case ecgLeisureHobbies:
395  switch (Content & 0x0F) {
396  default:
397  case 0x00: return tr("Content$Leisure/Hobbies");
398  case 0x01: return tr("Content$Tourism/Travel");
399  case 0x02: return tr("Content$Handicraft");
400  case 0x03: return tr("Content$Motoring");
401  case 0x04: return tr("Content$Fitness & Health");
402  case 0x05: return tr("Content$Cooking");
403  case 0x06: return tr("Content$Advertisement/Shopping");
404  case 0x07: return tr("Content$Gardening");
405  }
406  break;
407  case ecgSpecial:
408  switch (Content & 0x0F) {
409  case 0x00: return tr("Content$Original Language");
410  case 0x01: return tr("Content$Black & White");
411  case 0x02: return tr("Content$Unpublished");
412  case 0x03: return tr("Content$Live Broadcast");
413  default: ;
414  }
415  break;
416  default: ;
417  }
418  return "";
419 }
420 
422 {
423  if (parentalRating)
424  return cString::sprintf(tr("ParentalRating$from %d"), parentalRating);
425  return NULL;
426 }
427 
429 {
430  return DateString(startTime);
431 }
432 
434 {
435  return TimeString(startTime);
436 }
437 
439 {
440  return TimeString(startTime + duration);
441 }
442 
444 {
445  char buf[25];
446  struct tm tm_r;
447  strftime(buf, sizeof(buf), "%d.%m. %R", localtime_r(&vps, &tm_r));
448  return buf;
449 }
450 
451 void cEvent::Dump(FILE *f, const char *Prefix, bool InfoOnly) const
452 {
453  if (InfoOnly || startTime + duration + EPG_LINGER_TIME >= time(NULL)) {
454  fprintf(f, "%sE %u %ld %d %X %X\n", Prefix, eventID, startTime, duration, tableID, version);
455  if (!isempty(title))
456  fprintf(f, "%sT %s\n", Prefix, title);
457  if (!isempty(shortText))
458  fprintf(f, "%sS %s\n", Prefix, shortText);
459  if (!isempty(description)) {
460  strreplace(description, '\n', '|');
461  fprintf(f, "%sD %s\n", Prefix, description);
462  strreplace(description, '|', '\n');
463  }
464  if (contents[0]) {
465  fprintf(f, "%sG", Prefix);
466  for (int i = 0; Contents(i); i++)
467  fprintf(f, " %02X", Contents(i));
468  fprintf(f, "\n");
469  }
470  if (parentalRating)
471  fprintf(f, "%sR %d\n", Prefix, parentalRating);
472  if (components) {
473  for (int i = 0; i < components->NumComponents(); i++) {
475  fprintf(f, "%sX %s\n", Prefix, *p->ToString());
476  }
477  }
478  if (vps)
479  fprintf(f, "%sV %ld\n", Prefix, vps);
480  if (!InfoOnly && !isempty(aux)) {
481  strreplace(aux, '\n', '|');
482  fprintf(f, "%s@ %s\n", Prefix, aux);
483  strreplace(aux, '|', '\n');
484  }
485  if (!InfoOnly)
486  fprintf(f, "%se\n", Prefix);
487  }
488 }
489 
490 bool cEvent::Parse(char *s)
491 {
492  char *t = skipspace(s + 1);
493  switch (*s) {
494  case 'T': SetTitle(t);
495  break;
496  case 'S': SetShortText(t);
497  break;
498  case 'D': strreplace(t, '|', '\n');
499  SetDescription(t);
500  break;
501  case 'G': {
502  memset(contents, 0, sizeof(contents));
503  for (int i = 0; i < MaxEventContents; i++) {
504  char *tail = NULL;
505  int c = strtol(t, &tail, 16);
506  if (0x00 < c && c <= 0xFF) {
507  contents[i] = c;
508  t = tail;
509  }
510  else
511  break;
512  }
513  }
514  break;
515  case 'R': SetParentalRating(atoi(t));
516  break;
517  case 'X': if (!components)
518  components = new cComponents;
520  break;
521  case 'V': SetVps(atoi(t));
522  break;
523  case '@': strreplace(t, '|', '\n');
524  SetAux(t);
525  break;
526  default: esyslog("ERROR: unexpected tag while reading EPG data: %s", s);
527  return false;
528  }
529  return true;
530 }
531 
532 bool cEvent::Read(FILE *f, cSchedule *Schedule, int &Line)
533 {
534  if (Schedule) {
535  cEvent *Event = NULL;
536  char *s;
537  cReadLine ReadLine;
538  while ((s = ReadLine.Read(f)) != NULL) {
539  Line++;
540  char *t = skipspace(s + 1);
541  switch (*s) {
542  case 'E': if (!Event) {
543  unsigned int EventID;
544  time_t StartTime;
545  int Duration;
546  unsigned int TableID = 0;
547  unsigned int Version = 0xFF; // actual value is ignored
548  int n = sscanf(t, "%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
549  if (n >= 3 && n <= 5) {
551  cEvent *newEvent = NULL;
552  if (Event)
553  DELETENULL(Event->components);
554  if (!Event) {
555  Event = newEvent = new cEvent(EventID);
556  Event->seen = 0;
557  }
558  if (Event) {
559  Event->SetTableID(TableID);
560  Event->SetStartTime(StartTime);
561  Event->SetDuration(Duration);
562  if (newEvent)
563  Schedule->AddEvent(newEvent);
564  }
565  }
566  }
567  break;
568  case 'e': if (Event && !Event->Title())
569  Event->SetTitle(tr("No title"));
570  Event = NULL;
571  break;
572  case 'c': // to keep things simple we react on 'c' here
573  return true;
574  default: if (Event && !Event->Parse(s)) {
575  esyslog("ERROR: EPG data problem in line %d", Line);
576  return false;
577  }
578  }
579  }
580  esyslog("ERROR: unexpected end of file while reading EPG data");
581  }
582  return false;
583 }
584 
585 #define MAXEPGBUGFIXSTATS 13
586 #define MAXEPGBUGFIXCHANS 100
588  int hits;
589  int n;
591  tEpgBugFixStats(void) { hits = n = 0; }
592  };
593 
595 
596 static void EpgBugFixStat(int Number, tChannelID ChannelID)
597 {
598  if (0 <= Number && Number < MAXEPGBUGFIXSTATS) {
599  tEpgBugFixStats *p = &EpgBugFixStats[Number];
600  p->hits++;
601  int i = 0;
602  for (; i < p->n; i++) {
603  if (p->channelIDs[i] == ChannelID)
604  break;
605  }
606  if (i == p->n && p->n < MAXEPGBUGFIXCHANS)
607  p->channelIDs[p->n++] = ChannelID;
608  }
609 }
610 
611 void ReportEpgBugFixStats(bool Force)
612 {
613  if (Setup.EPGBugfixLevel > 0) {
614  static time_t LastReport = 0;
615  time_t now = time(NULL);
616  if (now - LastReport > 3600 || Force) {
617  LastReport = now;
618  struct tm tm_r;
619  struct tm *ptm = localtime_r(&now, &tm_r);
620  if (ptm->tm_hour != 5)
621  return;
622  }
623  else
624  return;
625  bool GotHits = false;
626  char buffer[1024];
627  for (int i = 0; i < MAXEPGBUGFIXSTATS; i++) {
628  const char *delim = " ";
630  if (p->hits) {
631  bool PrintedStats = false;
632  char *q = buffer;
633  *buffer = 0;
635  for (int c = 0; c < p->n; c++) {
636  if (const cChannel *Channel = Channels->GetByChannelID(p->channelIDs[c], true)) {
637  if (!GotHits) {
638  dsyslog("=====================");
639  dsyslog("EPG bugfix statistics");
640  dsyslog("=====================");
641  dsyslog("IF SOMEBODY WHO IS IN CHARGE OF THE EPG DATA FOR ONE OF THE LISTED");
642  dsyslog("CHANNELS READS THIS: PLEASE TAKE A LOOK AT THE FUNCTION cEvent::FixEpgBugs()");
643  dsyslog("IN VDR/epg.c TO LEARN WHAT'S WRONG WITH YOUR DATA, AND FIX IT!");
644  dsyslog("=====================");
645  dsyslog("Fix Hits Channels");
646  GotHits = true;
647  }
648  if (!PrintedStats) {
649  q += snprintf(q, sizeof(buffer) - (q - buffer), "%-3d %-4d", i, p->hits);
650  PrintedStats = true;
651  }
652  q += snprintf(q, sizeof(buffer) - (q - buffer), "%s%s", delim, Channel->Name());
653  delim = ", ";
654  if (q - buffer > 80) {
655  q += snprintf(q, sizeof(buffer) - (q - buffer), "%s...", delim);
656  break;
657  }
658  }
659  }
660  if (*buffer)
661  dsyslog("%s", buffer);
662  }
663  p->hits = p->n = 0;
664  }
665  if (GotHits)
666  dsyslog("=====================");
667  }
668 }
669 
670 static void StripControlCharacters(char *s)
671 {
672  if (s) {
673  int len = strlen(s);
674  while (len > 0) {
675  int l = Utf8CharLen(s);
676  uchar *p = (uchar *)s;
677  if (l == 2 && *p == 0xC2) // UTF-8 sequence
678  p++;
679  if (*p == 0x86 || *p == 0x87 || *p == 0x0D) {
680  memmove(s, p + 1, len - l + 1); // we also copy the terminating 0!
681  len -= l;
682  l = 0;
683  }
684  s += l;
685  len -= l;
686  }
687  }
688 }
689 
691 {
692  if (isempty(title)) {
693  // we don't want any "(null)" titles
694  title = strcpyrealloc(title, tr("No title"));
695  EpgBugFixStat(12, ChannelID());
696  }
697 
698  if (Setup.EPGBugfixLevel == 0)
699  goto Final;
700 
701  // Some TV stations apparently have their own idea about how to fill in the
702  // EPG data. Let's fix their bugs as good as we can:
703 
704  // Some channels put the ShortText in quotes and use either the ShortText
705  // or the Description field, depending on how long the string is:
706  //
707  // Title
708  // "ShortText". Description
709  //
710  if ((shortText == NULL) != (description == NULL)) {
711  char *p = shortText ? shortText : description;
712  if (*p == '"') {
713  const char *delim = "\".";
714  char *e = strstr(p + 1, delim);
715  if (e) {
716  *e = 0;
717  char *s = strdup(p + 1);
718  char *d = strdup(e + strlen(delim));
719  free(shortText);
720  free(description);
721  shortText = s;
722  description = d;
723  EpgBugFixStat(1, ChannelID());
724  }
725  }
726  }
727 
728  // Some channels put the Description into the ShortText (preceded
729  // by a blank) if there is no actual ShortText and the Description
730  // is short enough:
731  //
732  // Title
733  // Description
734  //
735  if (shortText && !description) {
736  if (*shortText == ' ') {
737  memmove(shortText, shortText + 1, strlen(shortText));
739  shortText = NULL;
740  EpgBugFixStat(2, ChannelID());
741  }
742  }
743 
744  // Sometimes they repeat the Title in the ShortText:
745  //
746  // Title
747  // Title
748  //
749  if (shortText && strcmp(title, shortText) == 0) {
750  free(shortText);
751  shortText = NULL;
752  EpgBugFixStat(3, ChannelID());
753  }
754 
755  // Some channels put the ShortText between double quotes, which is nothing
756  // but annoying (some even put a '.' after the closing '"'):
757  //
758  // Title
759  // "ShortText"[.]
760  //
761  if (shortText && *shortText == '"') {
762  int l = strlen(shortText);
763  if (l > 2 && (shortText[l - 1] == '"' || (shortText[l - 1] == '.' && shortText[l - 2] == '"'))) {
764  memmove(shortText, shortText + 1, l);
765  char *p = strrchr(shortText, '"');
766  if (p)
767  *p = 0;
768  EpgBugFixStat(4, ChannelID());
769  }
770  }
771 
772  if (Setup.EPGBugfixLevel <= 1)
773  goto Final;
774 
775  // Some channels apparently try to do some formatting in the texts,
776  // which is a bad idea because they have no way of knowing the width
777  // of the window that will actually display the text.
778  // Remove excess whitespace:
782 
783 #define MAX_USEFUL_EPISODE_LENGTH 40
784  // Some channels put a whole lot of information in the ShortText and leave
785  // the Description totally empty. So if the ShortText length exceeds
786  // MAX_USEFUL_EPISODE_LENGTH, let's put this into the Description
787  // instead:
788  if (!isempty(shortText) && isempty(description)) {
789  if (strlen(shortText) > MAX_USEFUL_EPISODE_LENGTH) {
790  free(description);
792  shortText = NULL;
793  EpgBugFixStat(6, ChannelID());
794  }
795  }
796 
797  // Some channels put the same information into ShortText and Description.
798  // In that case we delete one of them:
799  if (shortText && description && strcmp(shortText, description) == 0) {
800  if (strlen(shortText) > MAX_USEFUL_EPISODE_LENGTH) {
801  free(shortText);
802  shortText = NULL;
803  }
804  else {
805  free(description);
806  description = NULL;
807  }
808  EpgBugFixStat(7, ChannelID());
809  }
810 
811  // Some channels use the ` ("backtick") character, where a ' (single quote)
812  // would be normally used. Actually, "backticks" in normal text don't make
813  // much sense, so let's replace them:
814  strreplace(title, '`', '\'');
815  strreplace(shortText, '`', '\'');
816  strreplace(description, '`', '\'');
817 
818  if (Setup.EPGBugfixLevel <= 2)
819  goto Final;
820 
821  // The stream components have a "description" field which some channels
822  // apparently have no idea of how to set correctly:
823  if (components) {
824  for (int i = 0; i < components->NumComponents(); i++) {
826  switch (p->stream) {
827  case 0x01: { // video
828  if (p->description) {
829  if (strcasecmp(p->description, "Video") == 0 ||
830  strcasecmp(p->description, "Bildformat") == 0) {
831  // Yes, we know it's video - that's what the 'stream' code
832  // is for! But _which_ video is it?
833  free(p->description);
834  p->description = NULL;
835  EpgBugFixStat(8, ChannelID());
836  }
837  }
838  if (!p->description) {
839  switch (p->type) {
840  case 0x01:
841  case 0x05: p->description = strdup("4:3"); break;
842  case 0x02:
843  case 0x03:
844  case 0x06:
845  case 0x07: p->description = strdup("16:9"); break;
846  case 0x04:
847  case 0x08: p->description = strdup(">16:9"); break;
848  case 0x09:
849  case 0x0D: p->description = strdup("HD 4:3"); break;
850  case 0x0A:
851  case 0x0B:
852  case 0x0E:
853  case 0x0F: p->description = strdup("HD 16:9"); break;
854  case 0x0C:
855  case 0x10: p->description = strdup("HD >16:9"); break;
856  default: ;
857  }
858  EpgBugFixStat(9, ChannelID());
859  }
860  }
861  break;
862  case 0x02: { // audio
863  if (p->description) {
864  if (strcasecmp(p->description, "Audio") == 0) {
865  // Yes, we know it's audio - that's what the 'stream' code
866  // is for! But _which_ audio is it?
867  free(p->description);
868  p->description = NULL;
869  EpgBugFixStat(10, ChannelID());
870  }
871  }
872  if (!p->description) {
873  switch (p->type) {
874  case 0x05: p->description = strdup("Dolby Digital"); break;
875  default: ; // all others will just display the language
876  }
877  EpgBugFixStat(11, ChannelID());
878  }
879  }
880  break;
881  default: ;
882  }
883  }
884  }
885 
886 Final:
887 
888  // VDR can't usefully handle newline characters in the title, shortText or component description of EPG
889  // data, so let's always convert them to blanks (independent of the setting of EPGBugfixLevel):
890  strreplace(title, '\n', ' ');
891  strreplace(shortText, '\n', ' ');
892  if (components) {
893  for (int i = 0; i < components->NumComponents(); i++) {
895  if (p->description)
896  strreplace(p->description, '\n', ' ');
897  }
898  }
899  // Same for control characters:
903 }
904 
905 // --- cSchedule -------------------------------------------------------------
906 
908 
910 {
913  numTimers = 0;
914  hasRunning = false;
915  modified = 0;
916  onActualTp = false;
917  presentSeen = 0;
918 }
919 
920 void cSchedule::IncNumTimers(void) const
921 {
923  numTimers++;
925 }
926 
927 void cSchedule::DecNumTimers(void) const
928 {
930  numTimers--;
932 }
933 
935 {
936  if ((TableId & 0xF0) == 0x50)
937  onActualTp = true;
938  return onActualTp;
939 }
940 
942 {
943  events.Add(Event);
944  Event->schedule = this;
945  HashEvent(Event);
946  return Event;
947 }
948 
950 {
951  if (Event->schedule == this) {
952  UnhashEvent(Event);
953  Event->schedule = NULL;
954  // Removing the event from its schedule prevents it from decrementing the
955  // schedule's timer counter, so we do it here:
958  numTimers -= Event->numTimers;
961  events.Del(Event);
962  }
963 }
964 
966 {
967  if (cEvent *p = eventsHashID.Get(Event->EventID()))
968  eventsHashID.Del(p, p->EventID());
969  eventsHashID.Add(Event, Event->EventID());
970  if (Event->StartTime() > 0) { // 'StartTime < 0' is apparently used with NVOD channels
971  if (cEvent *p = eventsHashStartTime.Get(Event->StartTime()))
972  eventsHashStartTime.Del(p, p->StartTime());
973  eventsHashStartTime.Add(Event, Event->StartTime());
974  }
975 }
976 
978 {
979  eventsHashID.Del(Event, Event->EventID());
980  if (Event->StartTime() > 0) // 'StartTime < 0' is apparently used with NVOD channels
981  eventsHashStartTime.Del(Event, Event->StartTime());
982 }
983 
985 {
986  const cEvent *pe = NULL;
987  time_t now = time(NULL);
988  for (const cEvent *p = events.First(); p; p = events.Next(p)) {
989  if (p->StartTime() <= now)
990  pe = p;
991  else if (p->StartTime() > now + 3600)
992  break;
993  if (p->SeenWithin(RUNNINGSTATUSTIMEOUT) && p->RunningStatus() >= SI::RunningStatusPausing)
994  return p;
995  }
996  return pe;
997 }
998 
1000 {
1001  const cEvent *p = GetPresentEvent();
1002  if (p)
1003  p = events.Next(p);
1004  else {
1005  time_t now = time(NULL);
1006  for (p = events.First(); p; p = events.Next(p)) {
1007  if (p->StartTime() >= now)
1008  break;
1009  }
1010  }
1011  return p;
1012 }
1013 
1014 #if DEPRECATED_SCHEDULE_GET_EVENT
1015 const cEvent *cSchedule::GetEvent(tEventID EventID, time_t StartTime) const
1016 {
1017  // Returns the event info with the given StartTime or, if no actual StartTime
1018  // is given, the one with the given EventID.
1019  if (StartTime > 0) // 'StartTime < 0' is apparently used with NVOD channels
1020  return eventsHashStartTime.Get(StartTime);
1021  else
1022  return eventsHashID.Get(EventID);
1023 }
1024 #endif
1025 
1027 {
1028  return eventsHashID.Get(EventID);
1029 }
1030 
1031 const cEvent *cSchedule::GetEventByTime(time_t StartTime) const
1032 {
1033  if (StartTime > 0) // 'StartTime < 0' is apparently used with NVOD channels
1034  return eventsHashStartTime.Get(StartTime);
1035  return NULL;
1036 }
1037 
1038 const cEvent *cSchedule::GetEventAround(time_t Time) const
1039 {
1040  const cEvent *pe = NULL;
1041  time_t delta = INT_MAX;
1042  for (const cEvent *p = events.First(); p; p = events.Next(p)) {
1043  time_t dt = Time - p->StartTime();
1044  if (dt >= 0 && dt < delta && p->EndTime() >= Time) {
1045  delta = dt;
1046  pe = p;
1047  }
1048  }
1049  return pe;
1050 }
1051 
1053 {
1054  hasRunning = false;
1055  for (cEvent *p = events.First(); p; p = events.Next(p)) {
1056  if (p == Event) {
1058  p->SetRunningStatus(RunningStatus, Channel);
1059  break;
1060  }
1061  }
1062  else if (RunningStatus >= SI::RunningStatusPausing && p->StartTime() < Event->StartTime())
1063  p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
1064  if (p->RunningStatus() >= SI::RunningStatusPausing)
1065  hasRunning = true;
1066  }
1067  SetPresentSeen();
1068 }
1069 
1071 {
1072  if (hasRunning) {
1073  for (cEvent *p = events.First(); p; p = events.Next(p)) {
1074  if (p->RunningStatus() >= SI::RunningStatusPausing) {
1075  p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
1076  hasRunning = false;
1077  SetModified();
1078  break;
1079  }
1080  }
1081  }
1082 }
1083 
1085 {
1086  for (cEvent *p = events.First(); p; p = events.Next(p))
1087  p->SetVersion(0xFF);
1088 }
1089 
1091 {
1092  events.Sort();
1093  // Make sure there are no RunningStatusUndefined before the currently running event:
1094  if (hasRunning) {
1095  for (cEvent *p = events.First(); p; p = events.Next(p)) {
1096  if (p->RunningStatus() >= SI::RunningStatusPausing)
1097  break;
1098  p->SetRunningStatus(SI::RunningStatusNotRunning);
1099  }
1100  }
1101  SetModified();
1102 }
1103 
1104 void cSchedule::DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
1105 {
1106  // Events are sorted by start time.
1107  if (SegmentStart > 0 && SegmentEnd > 0) {
1108  cEvent *p = events.First();
1109  while (p) {
1110  cEvent *n = events.Next(p);
1111  if (p->StartTime() >= SegmentStart) {
1112  if (p->StartTime() < SegmentEnd) {
1113  // The event starts within the given time segment.
1114  if ((p->TableID() > 0x4E || TableID == 0x4E) && (p->TableID() != TableID || p->Version() != Version)) {
1115  // The segment overwrites all events from tables with other ids, and
1116  // within the same table id all events must have the same version.
1117  // Special consideration: table 0x4E can only be overwritten with the same id!
1118  DelEvent(p);
1119  }
1120  }
1121  else
1122  break;
1123  }
1124  p = n;
1125  }
1126  }
1127 }
1128 
1130 {
1131  Cleanup(time(NULL));
1132 }
1133 
1134 void cSchedule::Cleanup(time_t Time)
1135 {
1136  cEvent *Event;
1137  while ((Event = events.First()) != NULL) {
1138  if (!Event->HasTimer() && Event->EndTime() + EPG_LINGER_TIME < Time)
1139  DelEvent(Event);
1140  else
1141  break;
1142  }
1143 }
1144 
1145 void cSchedule::Dump(const cChannels *Channels, FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime) const
1146 {
1147  if (const cChannel *Channel = Channels->GetByChannelID(channelID, true)) {
1148  fprintf(f, "%sC %s %s\n", Prefix, *Channel->GetChannelID().ToString(), Channel->Name());
1149  const cEvent *p;
1150  switch (DumpMode) {
1151  case dmAll: {
1152  for (p = events.First(); p; p = events.Next(p))
1153  p->Dump(f, Prefix);
1154  }
1155  break;
1156  case dmPresent: {
1157  if ((p = GetPresentEvent()) != NULL)
1158  p->Dump(f, Prefix);
1159  }
1160  break;
1161  case dmFollowing: {
1162  if ((p = GetFollowingEvent()) != NULL)
1163  p->Dump(f, Prefix);
1164  }
1165  break;
1166  case dmAtTime: {
1167  if ((p = GetEventAround(AtTime)) != NULL)
1168  p->Dump(f, Prefix);
1169  }
1170  break;
1171  default: esyslog("ERROR: unknown DumpMode %d (%s %d)", DumpMode, __FUNCTION__, __LINE__);
1172  }
1173  fprintf(f, "%sc\n", Prefix);
1174  }
1175 }
1176 
1177 bool cSchedule::Read(FILE *f, cSchedules *Schedules)
1178 {
1179  if (Schedules) {
1180  int Line = 0;
1181  cReadLine ReadLine;
1182  char *s;
1183  while ((s = ReadLine.Read(f)) != NULL) {
1184  Line++;
1185  if (*s == 'C') {
1186  s = skipspace(s + 1);
1187  char *p = strchr(s, ' ');
1188  if (p)
1189  *p = 0; // strips optional channel name
1190  if (*s) {
1192  if (channelID.Valid()) {
1193  if (cSchedule *p = Schedules->AddSchedule(channelID)) {
1194  if (!cEvent::Read(f, p, Line))
1195  return false;
1196  p->Sort();
1197  }
1198  }
1199  else {
1200  esyslog("ERROR: invalid channel ID: %s", s);
1201  return false;
1202  }
1203  }
1204  }
1205  else {
1206  esyslog("ERROR: unexpected tag in line %d while reading EPG data: %s", Line, s);
1207  return false;
1208  }
1209  }
1210  return true;
1211  }
1212  return false;
1213 }
1214 
1215 // --- cEpgDataWriter --------------------------------------------------------
1216 
1217 class cEpgDataWriter : public cThread {
1218 private:
1220  bool dump;
1221 protected:
1222  virtual void Action(void);
1223 public:
1224  cEpgDataWriter(void);
1225  void SetDump(bool Dump) { dump = Dump; }
1226  void Perform(void);
1227  };
1228 
1230 :cThread("epg data writer", true)
1231 {
1232  dump = false;
1233 }
1234 
1236 {
1237  Perform();
1238 }
1239 
1241 {
1242  cMutexLock MutexLock(&mutex); // to make sure fore- and background calls don't cause parellel dumps!
1243  {
1244  cStateKey StateKey;
1245  if (cSchedules *Schedules = cSchedules::GetSchedulesWrite(StateKey, 1000)) {
1246  time_t now = time(NULL);
1247  for (cSchedule *p = Schedules->First(); p; p = Schedules->Next(p))
1248  p->Cleanup(now);
1249  StateKey.Remove();
1250  }
1251  }
1252  if (dump)
1253  cSchedules::Dump();
1254 }
1255 
1257 
1258 // --- cSchedules ------------------------------------------------------------
1259 
1261 char *cSchedules::epgDataFileName = NULL;
1262 time_t cSchedules::lastDump = time(NULL);
1263 
1265 :cList<cSchedule>("5 Schedules")
1266 {
1267 }
1268 
1269 const cSchedules *cSchedules::GetSchedulesRead(cStateKey &StateKey, int TimeoutMs)
1270 {
1271  return schedules.Lock(StateKey, false, TimeoutMs) ? &schedules : NULL;
1272 }
1273 
1275 {
1276  return schedules.Lock(StateKey, true, TimeoutMs) ? &schedules : NULL;
1277 }
1278 
1279 void cSchedules::SetEpgDataFileName(const char *FileName)
1280 {
1281  free(epgDataFileName);
1282  epgDataFileName = FileName ? strdup(FileName) : NULL;
1284 }
1285 
1286 void cSchedules::Cleanup(bool Force)
1287 {
1288  if (Force)
1289  lastDump = 0;
1290  time_t now = time(NULL);
1291  if (now - lastDump > EPGDATAWRITEDELTA) {
1292  if (Force)
1294  else if (!EpgDataWriter.Active())
1295  EpgDataWriter.Start();
1296  lastDump = now;
1297  }
1298 }
1299 
1301 {
1303  for (cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->Next(Schedule))
1304  Schedule->ResetVersions();
1305 }
1306 
1307 bool cSchedules::Dump(FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime)
1308 {
1309  cSafeFile *sf = NULL;
1310  if (!f) {
1311  sf = new cSafeFile(epgDataFileName);
1312  if (sf->Open())
1313  f = *sf;
1314  else {
1315  LOG_ERROR;
1316  delete sf;
1317  return false;
1318  }
1319  }
1322  for (const cSchedule *p = Schedules->First(); p; p = Schedules->Next(p))
1323  p->Dump(Channels, f, Prefix, DumpMode, AtTime);
1324  if (sf) {
1325  sf->Close();
1326  delete sf;
1327  }
1328  return true;
1329 }
1330 
1331 bool cSchedules::Read(FILE *f)
1332 {
1333  bool OwnFile = f == NULL;
1334  if (OwnFile) {
1335  if (epgDataFileName && access(epgDataFileName, R_OK) == 0) {
1336  dsyslog("reading EPG data from %s", epgDataFileName);
1337  if ((f = fopen(epgDataFileName, "r")) == NULL) {
1338  LOG_ERROR;
1339  return false;
1340  }
1341  }
1342  else
1343  return false;
1344  }
1347  bool result = cSchedule::Read(f, Schedules);
1348  if (OwnFile)
1349  fclose(f);
1350  if (result) {
1351  // Initialize the channels' schedule pointers, so that the first WhatsOn menu will come up faster:
1352  for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) {
1353  if (const cSchedule *Schedule = Channel->schedule) {
1354  if (!Schedule->ChannelID().Valid()) // this is the DummySchedule
1355  Channel->schedule = NULL;
1356  }
1357  Schedules->GetSchedule(Channel);
1358  }
1359  }
1360  return result;
1361 }
1362 
1364 {
1365  ChannelID.ClrRid();
1366  cSchedule *p = (cSchedule *)GetSchedule(ChannelID);
1367  if (!p) {
1368  p = new cSchedule(ChannelID);
1369  Add(p);
1370  }
1371  return p;
1372 }
1373 
1375 {
1376  ChannelID.ClrRid();
1377  for (const cSchedule *p = First(); p; p = Next(p)) {
1378  if (p->ChannelID() == ChannelID)
1379  return p;
1380  }
1381  return NULL;
1382 }
1383 
1384 const cSchedule *cSchedules::GetSchedule(const cChannel *Channel, bool AddIfMissing) const
1385 {
1386  // This is not very beautiful, but it dramatically speeds up the
1387  // "What's on now/next?" menus.
1388  if (!Channel)
1389  return NULL;
1390  static cSchedule DummySchedule(tChannelID::InvalidID);
1391  if (!Channel->schedule)
1392  Channel->schedule = GetSchedule(Channel->GetChannelID());
1393  if (!Channel->schedule)
1394  Channel->schedule = &DummySchedule;
1395  if (Channel->schedule == &DummySchedule && AddIfMissing) {
1396  cSchedule *Schedule = new cSchedule(Channel->GetChannelID());
1397  ((cSchedules *)this)->Add(Schedule);
1398  Channel->schedule = Schedule;
1399  }
1400  return Channel->schedule != &DummySchedule? Channel->schedule : NULL;
1401 }
1402 
1403 // --- cEpgDataReader --------------------------------------------------------
1404 
1406 :cThread("epg data reader")
1407 {
1408 }
1409 
1411 {
1412  cSchedules::Read();
1413 }
1414 
1415 // --- cEpgHandler -----------------------------------------------------------
1416 
1418 {
1419  EpgHandlers.Add(this);
1420 }
1421 
1423 {
1424  EpgHandlers.Del(this, false);
1425 }
1426 
1427 // --- cEpgHandlers ----------------------------------------------------------
1428 
1430 
1432 {
1433  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1434  if (eh->IgnoreChannel(Channel))
1435  return true;
1436  }
1437  return false;
1438 }
1439 
1440 bool cEpgHandlers::HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version)
1441 {
1442  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1443  if (eh->HandleEitEvent(Schedule, EitEvent, TableID, Version))
1444  return true;
1445  }
1446  return false;
1447 }
1448 
1450 {
1451  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1452  if (eh->HandledExternally(Channel))
1453  return true;
1454  }
1455  return false;
1456 }
1457 
1458 bool cEpgHandlers::IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version)
1459 {
1460  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1461  if (eh->IsUpdate(EventID, StartTime, TableID, Version))
1462  return true;
1463  }
1464  return false;
1465 }
1466 
1468 {
1469  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1470  if (eh->SetEventID(Event, EventID))
1471  return;
1472  }
1473  Event->SetEventID(EventID);
1474 }
1475 
1476 void cEpgHandlers::SetTitle(cEvent *Event, const char *Title)
1477 {
1478  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1479  if (eh->SetTitle(Event, Title))
1480  return;
1481  }
1482  Event->SetTitle(Title);
1483 }
1484 
1485 void cEpgHandlers::SetShortText(cEvent *Event, const char *ShortText)
1486 {
1487  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1488  if (eh->SetShortText(Event, ShortText))
1489  return;
1490  }
1491  Event->SetShortText(ShortText);
1492 }
1493 
1494 void cEpgHandlers::SetDescription(cEvent *Event, const char *Description)
1495 {
1496  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1497  if (eh->SetDescription(Event, Description))
1498  return;
1499  }
1500  Event->SetDescription(Description);
1501 }
1502 
1503 void cEpgHandlers::SetContents(cEvent *Event, uchar *Contents)
1504 {
1505  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1506  if (eh->SetContents(Event, Contents))
1507  return;
1508  }
1509  Event->SetContents(Contents);
1510 }
1511 
1512 void cEpgHandlers::SetParentalRating(cEvent *Event, int ParentalRating)
1513 {
1514  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1515  if (eh->SetParentalRating(Event, ParentalRating))
1516  return;
1517  }
1518  Event->SetParentalRating(ParentalRating);
1519 }
1520 
1521 void cEpgHandlers::SetStartTime(cEvent *Event, time_t StartTime)
1522 {
1523  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1524  if (eh->SetStartTime(Event, StartTime))
1525  return;
1526  }
1527  Event->SetStartTime(StartTime);
1528 }
1529 
1530 void cEpgHandlers::SetDuration(cEvent *Event, int Duration)
1531 {
1532  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1533  if (eh->SetDuration(Event, Duration))
1534  return;
1535  }
1536  Event->SetDuration(Duration);
1537 }
1538 
1539 void cEpgHandlers::SetVps(cEvent *Event, time_t Vps)
1540 {
1541  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1542  if (eh->SetVps(Event, Vps))
1543  return;
1544  }
1545  Event->SetVps(Vps);
1546 }
1547 
1549 {
1550  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1551  if (eh->SetComponents(Event, Components))
1552  return;
1553  }
1554  Event->SetComponents(Components);
1555 }
1556 
1558 {
1559  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1560  if (eh->FixEpgBugs(Event))
1561  return;
1562  }
1563  Event->FixEpgBugs();
1564 }
1565 
1567 {
1568  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1569  if (eh->HandleEvent(Event))
1570  break;
1571  }
1572 }
1573 
1575 {
1576  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1577  if (eh->SortSchedule(Schedule))
1578  return;
1579  }
1580  Schedule->Sort();
1581 }
1582 
1583 void cEpgHandlers::DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
1584 {
1585  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1586  if (eh->DropOutdated(Schedule, SegmentStart, SegmentEnd, TableID, Version))
1587  return;
1588  }
1589  Schedule->DropOutdated(SegmentStart, SegmentEnd, TableID, Version);
1590 }
1591 
1593 {
1594  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1595  if (!eh->BeginSegmentTransfer(Channel, false))
1596  return false;
1597  }
1598  return true;
1599 }
1600 
1602 {
1603  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1604  if (eh->EndSegmentTransfer(Modified, false))
1605  return;
1606  }
1607 }
#define LOCK_CHANNELS_READ
Definition: channels.h:269
#define LOCK_CHANNELS_WRITE
Definition: channels.h:270
int Number(void) const
Definition: channels.h:178
const char * Name(void) const
Definition: channels.c:107
tChannelID GetChannelID(void) const
Definition: channels.h:190
const cSchedule * schedule
Definition: channels.h:132
const cChannel * GetByChannelID(tChannelID ChannelID, bool TryWithoutRid=false, bool TryWithoutPolarization=false) const
Definition: channels.c:1010
tComponent * Component(int Index) const
Definition: epg.h:64
tComponent * GetComponent(int Index, uchar Stream, uchar Type)
Definition: epg.c:97
int numComponents
Definition: epg.h:55
cComponents(void)
Definition: epg.c:46
bool Realloc(int Index)
Definition: epg.c:59
~cComponents(void)
Definition: epg.c:52
int NumComponents(void) const
Definition: epg.h:61
tComponent * components
Definition: epg.h:56
void SetComponent(int Index, const char *s)
Definition: epg.c:77
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: epg.c:1410
cEpgDataReader(void)
Definition: epg.c:1405
void Perform(void)
Definition: epg.c:1240
void SetDump(bool Dump)
Definition: epg.c:1225
bool dump
Definition: epg.c:1220
cEpgDataWriter(void)
Definition: epg.c:1229
cMutex mutex
Definition: epg.c:1219
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: epg.c:1235
cEpgHandler(void)
Constructs a new EPG handler and adds it to the list of EPG handlers.
Definition: epg.c:1417
virtual ~cEpgHandler()
Definition: epg.c:1422
void SortSchedule(cSchedule *Schedule)
Definition: epg.c:1574
void EndSegmentTransfer(bool Modified)
Definition: epg.c:1601
bool IgnoreChannel(const cChannel *Channel)
Definition: epg.c:1431
bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version)
Definition: epg.c:1440
void SetStartTime(cEvent *Event, time_t StartTime)
Definition: epg.c:1521
void SetTitle(cEvent *Event, const char *Title)
Definition: epg.c:1476
void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
Definition: epg.c:1583
bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version)
Definition: epg.c:1458
void FixEpgBugs(cEvent *Event)
Definition: epg.c:1557
void HandleEvent(cEvent *Event)
Definition: epg.c:1566
void SetComponents(cEvent *Event, cComponents *Components)
Definition: epg.c:1548
void SetVps(cEvent *Event, time_t Vps)
Definition: epg.c:1539
void SetParentalRating(cEvent *Event, int ParentalRating)
Definition: epg.c:1512
bool BeginSegmentTransfer(const cChannel *Channel)
Definition: epg.c:1592
bool HandledExternally(const cChannel *Channel)
Definition: epg.c:1449
void SetContents(cEvent *Event, uchar *Contents)
Definition: epg.c:1503
void SetShortText(cEvent *Event, const char *ShortText)
Definition: epg.c:1485
void SetDuration(cEvent *Event, int Duration)
Definition: epg.c:1530
void SetDescription(cEvent *Event, const char *Description)
Definition: epg.c:1494
void SetEventID(cEvent *Event, tEventID EventID)
Definition: epg.c:1467
Definition: epg.h:73
char * shortText
Definition: epg.h:86
cString ToDescr(void) const
Definition: epg.c:248
~cEvent()
Definition: epg.c:136
time_t Vps(void) const
Definition: epg.h:114
time_t vps
Definition: epg.h:92
static const char * ContentToString(uchar Content)
Definition: epg.c:279
void SetSeen(void)
Definition: epg.c:237
uchar TableID(void) const
Definition: epg.h:102
void SetAux(const char *Aux)
Definition: epg.c:242
const char * Aux(void) const
Definition: epg.h:117
time_t EndTime(void) const
Definition: epg.h:112
static cMutex numTimersMutex
Definition: epg.h:76
uchar parentalRating
Definition: epg.h:84
cString GetDateString(void) const
Definition: epg.c:428
int RunningStatus(void) const
Definition: epg.h:104
uchar Contents(int i=0) const
Definition: epg.h:109
bool IsRunning(bool OrAboutToStart=false) const
Definition: epg.c:274
cEvent(tEventID EventID)
Definition: epg.c:115
void SetRunningStatus(int RunningStatus, const cChannel *Channel=NULL)
Definition: epg.c:177
void IncNumTimers(void) const
Definition: epg.c:256
int ParentalRating(void) const
Definition: epg.h:110
time_t StartTime(void) const
Definition: epg.h:111
tChannelID ChannelID(void) const
Definition: epg.c:151
void SetVps(time_t Vps)
Definition: epg.c:232
bool Parse(char *s)
Definition: epg.c:490
char * title
Definition: epg.h:85
static bool Read(FILE *f, cSchedule *Schedule, int &Line)
Definition: epg.c:532
time_t seen
Definition: epg.h:93
char * description
Definition: epg.h:87
tEventID eventID
Definition: epg.h:80
void SetShortText(const char *ShortText)
Definition: epg.c:189
u_int16_t numTimers
Definition: epg.h:79
cString GetTimeString(void) const
Definition: epg.c:433
const cComponents * Components(void) const
Definition: epg.h:108
void DecNumTimers(void) const
Definition: epg.c:265
tEventID EventID(void) const
Definition: epg.h:101
virtual int Compare(const cListObject &ListObject) const
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
Definition: epg.c:145
cComponents * components
Definition: epg.h:88
void SetStartTime(time_t StartTime)
Definition: epg.c:216
bool HasTimer(void) const
Definition: epg.h:120
const char * Title(void) const
Definition: epg.h:105
void SetComponents(cComponents *Components)
Definition: epg.c:199
int duration
Definition: epg.h:90
void SetEventID(tEventID EventID)
Definition: epg.c:156
const cSchedule * Schedule(void) const
Definition: epg.h:100
cString GetEndTimeString(void) const
Definition: epg.c:438
int Duration(void) const
Definition: epg.h:113
cString GetVpsString(void) const
Definition: epg.c:443
void SetVersion(uchar Version)
Definition: epg.c:172
uchar tableID
Definition: epg.h:81
void Dump(FILE *f, const char *Prefix="", bool InfoOnly=false) const
Definition: epg.c:451
void SetDuration(int Duration)
Definition: epg.c:227
void SetContents(uchar *Contents)
Definition: epg.c:205
cSchedule * schedule
Definition: epg.h:78
uchar Version(void) const
Definition: epg.h:103
const char * ShortText(void) const
Definition: epg.h:106
uchar runningStatus
Definition: epg.h:83
void SetTitle(const char *Title)
Definition: epg.c:184
char * aux
Definition: epg.h:94
uchar version
Definition: epg.h:82
void SetTableID(uchar TableID)
Definition: epg.c:167
uchar contents[MaxEventContents]
Definition: epg.h:91
cString GetParentalRatingString(void) const
Definition: epg.c:421
void FixEpgBugs(void)
Definition: epg.c:690
void SetDescription(const char *Description)
Definition: epg.c:194
const char * Description(void) const
Definition: epg.h:107
time_t startTime
Definition: epg.h:89
void SetParentalRating(int ParentalRating)
Definition: epg.c:211
void Del(cListObject *Object, unsigned int Id)
Definition: tools.c:2391
void Add(cListObject *Object, unsigned int Id)
Definition: tools.c:2383
T * Get(unsigned int Id) const
Definition: tools.h:929
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2216
void SetUseGarbageCollector(void)
Definition: tools.h:614
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0) const
Tries to get a lock on this list and returns true if successful.
Definition: tools.c:2175
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2184
void Sort(void)
Definition: tools.c:2308
cListObject * Next(void) const
Definition: tools.h:557
Definition: tools.h:641
const T * Next(const T *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition: tools.h:660
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:653
Definition: thread.h:67
void Lock(void)
Definition: thread.c:222
void Unlock(void)
Definition: thread.c:228
char * Read(FILE *f)
Definition: tools.c:1481
bool Open(void)
Definition: tools.c:1772
bool Close(void)
Definition: tools.c:1782
Definition: epg.h:152
const cEvent * GetPresentEvent(void) const
Definition: epg.c:984
cHash< cEvent > eventsHashID
Definition: epg.h:157
bool HasTimer(void) const
Definition: epg.h:182
void SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel=NULL)
Definition: epg.c:1052
void UnhashEvent(cEvent *Event)
Definition: epg.c:977
const cEvent * GetEventAround(time_t Time) const
Definition: epg.c:1038
const cEvent * GetEventByTime(time_t StartTime) const
Definition: epg.c:1031
void DecNumTimers(void) const
Definition: epg.c:927
const cEvent * GetEvent(tEventID EventID, time_t StartTime=0) const
Definition: epg.c:1015
bool OnActualTp(uchar TableId)
Definition: epg.c:934
void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
Definition: epg.c:1104
static bool Read(FILE *f, cSchedules *Schedules)
Definition: epg.c:1177
cSchedule(tChannelID ChannelID)
Definition: epg.c:909
void SetPresentSeen(void)
Definition: epg.h:172
static cMutex numTimersMutex
Definition: epg.h:154
void ClrRunningStatus(cChannel *Channel=NULL)
Definition: epg.c:1070
void ResetVersions(void)
Definition: epg.c:1084
cList< cEvent > events
Definition: epg.h:156
void Cleanup(void)
Definition: epg.c:1129
tChannelID channelID
Definition: epg.h:155
const cEvent * GetEventById(tEventID EventID) const
Definition: epg.c:1026
void DelEvent(cEvent *Event)
Definition: epg.c:949
void HashEvent(cEvent *Event)
Definition: epg.c:965
u_int16_t numTimers
Definition: epg.h:159
tChannelID ChannelID(void) const
Definition: epg.h:166
void SetModified(void)
Definition: epg.h:171
int modified
Definition: epg.h:162
void Sort(void)
Definition: epg.c:1090
bool onActualTp
Definition: epg.h:161
cHash< cEvent > eventsHashStartTime
Definition: epg.h:158
void IncNumTimers(void) const
Definition: epg.c:920
time_t presentSeen
Definition: epg.h:163
cEvent * AddEvent(cEvent *Event)
Definition: epg.c:941
void Dump(const cChannels *Channels, FILE *f, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0) const
Definition: epg.c:1145
bool hasRunning
Definition: epg.h:160
const cEvent * GetFollowingEvent(void) const
Definition: epg.c:999
cSchedules(void)
Definition: epg.c:1264
static cSchedules * GetSchedulesWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of schedules for write access.
Definition: epg.c:1274
const cSchedule * GetSchedule(tChannelID ChannelID) const
Definition: epg.c:1374
static const cSchedules * GetSchedulesRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of schedules for read access.
Definition: epg.c:1269
static bool Dump(FILE *f=NULL, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0)
Definition: epg.c:1307
static void SetEpgDataFileName(const char *FileName)
Definition: epg.c:1279
static void Cleanup(bool Force=false)
Definition: epg.c:1286
static time_t lastDump
Definition: epg.h:206
static char * epgDataFileName
Definition: epg.h:205
static void ResetVersions(void)
Definition: epg.c:1300
cSchedule * AddSchedule(tChannelID ChannelID)
Definition: epg.c:1363
static bool Read(FILE *f=NULL)
Definition: epg.c:1331
static cSchedules schedules
Definition: epg.h:204
friend class cSchedule
Definition: epg.h:202
int EPGBugfixLevel
Definition: config.h:299
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
Definition: thread.c:859
Definition: tools.h:178
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1149
Definition: thread.h:79
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition: thread.c:304
bool Active(void)
Checks whether the thread is still alive.
Definition: thread.c:329
cSetup Setup
Definition: config.c:372
#define EPGDATAWRITEDELTA
Definition: epg.c:20
cEpgHandlers EpgHandlers
Definition: epg.c:1429
static void StripControlCharacters(char *s)
Definition: epg.c:670
tEpgBugFixStats EpgBugFixStats[MAXEPGBUGFIXSTATS]
Definition: epg.c:594
void ReportEpgBugFixStats(bool Force)
Definition: epg.c:611
#define MAXEPGBUGFIXCHANS
Definition: epg.c:586
#define MAX_USEFUL_EPISODE_LENGTH
#define RUNNINGSTATUSTIMEOUT
Definition: epg.c:19
#define MAXEPGBUGFIXSTATS
Definition: epg.c:585
static void EpgBugFixStat(int Number, tChannelID ChannelID)
Definition: epg.c:596
static cEpgDataWriter EpgDataWriter
Definition: epg.c:1256
@ MaxEventContents
Definition: epg.h:25
#define LOCK_SCHEDULES_READ
Definition: epg.h:233
u_int32_t tEventID
Definition: epg.h:69
eDumpMode
Definition: epg.h:42
@ dmAtTime
Definition: epg.h:42
@ dmPresent
Definition: epg.h:42
@ dmFollowing
Definition: epg.h:42
@ dmAll
Definition: epg.h:42
#define LOCK_SCHEDULES_WRITE
Definition: epg.h:234
#define EPG_LINGER_TIME
Definition: epg.h:23
@ ecgSocialPoliticalEconomics
Definition: epg.h:35
@ ecgNewsCurrentAffairs
Definition: epg.h:29
@ ecgEducationalScience
Definition: epg.h:36
@ ecgMovieDrama
Definition: epg.h:28
@ ecgArtsCulture
Definition: epg.h:34
@ ecgShow
Definition: epg.h:30
@ ecgSports
Definition: epg.h:31
@ ecgLeisureHobbies
Definition: epg.h:37
@ ecgMusicBalletDance
Definition: epg.h:33
@ ecgSpecial
Definition: epg.h:38
@ ecgChildrenYouth
Definition: epg.h:32
#define tr(s)
Definition: i18n.h:85
static int Utf8CharLen(const char *s)
Definition: si.c:400
RunningStatus
Definition: si.h:197
@ RunningStatusUndefined
Definition: si.h:197
@ RunningStatusPausing
Definition: si.h:200
@ RunningStatusNotRunning
Definition: si.h:198
@ RunningStatusStartsInAFewSeconds
Definition: si.h:199
TableId
Definition: si.h:23
tChannelID & ClrRid(void)
Definition: channels.h:59
static const tChannelID InvalidID
Definition: channels.h:68
bool Valid(void) const
Definition: channels.h:58
static tChannelID FromString(const char *s)
Definition: channels.c:23
Definition: epg.h:44
bool FromString(const char *s)
Definition: epg.c:31
char language[MAXLANGCODE2]
Definition: epg.h:47
uchar stream
Definition: epg.h:45
cString ToString(void)
Definition: epg.c:24
uchar type
Definition: epg.h:46
char * description
Definition: epg.h:48
tChannelID channelIDs[MAXEPGBUGFIXCHANS]
Definition: epg.c:590
tEpgBugFixStats(void)
Definition: epg.c:591
int hits
Definition: epg.c:588
cString TimeString(time_t t)
Converts the given time to a string of the form "hh:mm".
Definition: tools.c:1255
char * strcpyrealloc(char *dest, const char *src)
Definition: tools.c:114
bool isempty(const char *s)
Definition: tools.c:349
char * strreplace(char *s, char c1, char c2)
Definition: tools.c:139
cString DateString(time_t t)
Converts the given time to a string of the form "www dd.mm.yyyy".
Definition: tools.c:1235
char * strn0cpy(char *dest, const char *src, size_t n)
Definition: tools.c:131
char * compactspace(char *s)
Definition: tools.c:231
char * skipspace(const char *s)
Definition: tools.h:241
unsigned char uchar
Definition: tools.h:31
#define dsyslog(a...)
Definition: tools.h:37
void DELETENULL(T *&p)
Definition: tools.h:49
#define esyslog(a...)
Definition: tools.h:35
#define LOG_ERROR
Definition: tools.h:39
#define isyslog(a...)
Definition: tools.h:36