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