vdr 2.6.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.9 2024/06/21 06:27:20 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 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
145int 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);
162 if (schedule)
163 schedule->HashEvent(this);
164 }
165}
166
168{
170}
171
173{
175}
176
177void cEvent::SetRunningStatus(int RunningStatus, const cChannel *Channel)
178{
180 isyslog("channel %d (%s) event %s status %d->%d", Channel->Number(), Channel->Name(), *ToDescr(), runningStatus, RunningStatus);
182}
183
184void cEvent::SetTitle(const char *Title)
185{
187}
188
189void cEvent::SetShortText(const char *ShortText)
190{
192}
193
194void cEvent::SetDescription(const char *Description)
195{
197}
198
200{
201 delete components;
203}
204
206{
207 for (int i = 0; i < MaxEventContents; i++)
208 contents[i] = Contents[i];
209}
210
211void cEvent::SetParentalRating(int ParentalRating)
212{
214}
215
216void 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
227void cEvent::SetDuration(int Duration)
228{
230}
231
232void cEvent::SetVps(time_t Vps)
233{
234 vps = Vps;
235}
236
238{
239 seen = time(NULL);
240}
241
242void 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
256void cEvent::IncNumTimers(void) const
257{
259 numTimers++;
260 if (schedule)
263}
264
265void cEvent::DecNumTimers(void) const
266{
268 numTimers--;
269 if (schedule)
272}
273
274bool cEvent::IsRunning(bool OrAboutToStart) const
275{
277}
278
279const 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;
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;
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
451void 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 %jd %d %X %X\n", Prefix, eventID, intmax_t(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 %jd\n", Prefix, intmax_t(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
490bool 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');
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)
520 break;
521 case 'V': SetVps(atol(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
532bool 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 intmax_t StartTime; // actually time_t, but intmax_t for scanning with "%jd"
545 int Duration;
546 unsigned int TableID = 0;
547 unsigned int Version = 0xFF; // actual value is ignored
548 int n = sscanf(t, "%u %jd %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
593
595
596static 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
611void 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
670static 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"));
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;
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;
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;
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;
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:
789 if (strlen(shortText) > MAX_USEFUL_EPISODE_LENGTH) {
790 free(description);
792 shortText = NULL;
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 }
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;
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 }
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;
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 }
878 }
879 }
880 break;
881 default: ;
882 }
883 }
884 }
885
886Final:
887
888 // And then there are the specially gifted people who put a literal "\n" string where there should be
889 // a '\n' character:
890 if (shortText) {
891 if (char *p = strstr(shortText, "\\n")) {
892 *p = 0;
893 p += 2;
894 char *s = strdup(shortText);
895 char *d = strdup(cString::sprintf("%s\n\n%s", p, description));
896 free(shortText);
897 free(description);
898 shortText = s;
899 description = d;
901 }
902 }
903 description = strreplace(description, "\\n", " \n");
904
905 // VDR can't usefully handle newline characters in the title, shortText or component description of EPG
906 // data, so let's always convert them to blanks (independent of the setting of EPGBugfixLevel):
907 strreplace(title, '\n', ' ');
908 strreplace(shortText, '\n', ' ');
909 if (components) {
910 for (int i = 0; i < components->NumComponents(); i++) {
912 if (p->description)
913 strreplace(p->description, '\n', ' ');
914 }
915 }
916 // Same for control characters:
920}
921
922// --- cSchedule -------------------------------------------------------------
923
925
927{
930 numTimers = 0;
931 hasRunning = false;
932 modified = 0;
933 onActualTp = false;
934 presentSeen = 0;
935}
936
938{
940 numTimers++;
942}
943
945{
947 numTimers--;
949}
950
952{
953 if ((TableId & 0xF0) == 0x50)
954 onActualTp = true;
955 return onActualTp;
956}
957
959{
960 events.Add(Event);
961 Event->schedule = this;
962 HashEvent(Event);
963 return Event;
964}
965
967{
968 if (Event->schedule == this) {
969 UnhashEvent(Event);
970 Event->schedule = NULL;
971 // Removing the event from its schedule prevents it from decrementing the
972 // schedule's timer counter, so we do it here:
975 numTimers -= Event->numTimers;
978 events.Del(Event);
979 }
980}
981
983{
984 if (cEvent *p = eventsHashID.Get(Event->EventID()))
985 eventsHashID.Del(p, p->EventID());
986 eventsHashID.Add(Event, Event->EventID());
987 if (Event->StartTime() > 0) { // 'StartTime < 0' is apparently used with NVOD channels
988 if (cEvent *p = eventsHashStartTime.Get(Event->StartTime()))
989 eventsHashStartTime.Del(p, p->StartTime());
990 eventsHashStartTime.Add(Event, Event->StartTime());
991 }
992}
993
995{
996 eventsHashID.Del(Event, Event->EventID());
997 if (Event->StartTime() > 0) // 'StartTime < 0' is apparently used with NVOD channels
998 eventsHashStartTime.Del(Event, Event->StartTime());
999}
1000
1002{
1003 const cEvent *pe = NULL;
1004 time_t now = time(NULL);
1005 for (const cEvent *p = events.First(); p; p = events.Next(p)) {
1006 if (p->StartTime() <= now)
1007 pe = p;
1008 else if (p->StartTime() > now + 3600)
1009 break;
1010 if (p->SeenWithin(RUNNINGSTATUSTIMEOUT) && p->RunningStatus() >= SI::RunningStatusPausing)
1011 return p;
1012 }
1013 return pe;
1014}
1015
1017{
1018 const cEvent *p = GetPresentEvent();
1019 if (p)
1020 p = events.Next(p);
1021 else {
1022 time_t now = time(NULL);
1023 for (p = events.First(); p; p = events.Next(p)) {
1024 if (p->StartTime() >= now)
1025 break;
1026 }
1027 }
1028 return p;
1029}
1030
1031#if DEPRECATED_SCHEDULE_GET_EVENT
1032const cEvent *cSchedule::GetEvent(tEventID EventID, time_t StartTime) const
1033{
1034 // Returns the event info with the given StartTime or, if no actual StartTime
1035 // is given, the one with the given EventID.
1036 if (StartTime > 0) // 'StartTime < 0' is apparently used with NVOD channels
1037 return eventsHashStartTime.Get(StartTime);
1038 else
1039 return eventsHashID.Get(EventID);
1040}
1041#endif
1042
1044{
1045 return eventsHashID.Get(EventID);
1046}
1047
1048const cEvent *cSchedule::GetEventByTime(time_t StartTime) const
1049{
1050 if (StartTime > 0) // 'StartTime < 0' is apparently used with NVOD channels
1051 return eventsHashStartTime.Get(StartTime);
1052 return NULL;
1053}
1054
1055const cEvent *cSchedule::GetEventAround(time_t Time) const
1056{
1057 const cEvent *pe = NULL;
1058 time_t delta = INT_MAX;
1059 for (const cEvent *p = events.First(); p; p = events.Next(p)) {
1060 time_t dt = Time - p->StartTime();
1061 if (dt >= 0 && dt < delta && p->EndTime() >= Time) {
1062 delta = dt;
1063 pe = p;
1064 }
1065 }
1066 return pe;
1067}
1068
1069void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel)
1070{
1071 hasRunning = false;
1072 for (cEvent *p = events.First(); p; p = events.Next(p)) {
1073 if (p == Event) {
1074 if (p->RunningStatus() > SI::RunningStatusNotRunning || RunningStatus > SI::RunningStatusNotRunning) {
1075 p->SetRunningStatus(RunningStatus, Channel);
1076 break;
1077 }
1078 }
1079 else if (RunningStatus >= SI::RunningStatusPausing && p->StartTime() < Event->StartTime())
1080 p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
1081 if (p->RunningStatus() >= SI::RunningStatusPausing)
1082 hasRunning = true;
1083 }
1085}
1086
1088{
1089 if (hasRunning) {
1090 for (cEvent *p = events.First(); p; p = events.Next(p)) {
1091 if (p->RunningStatus() >= SI::RunningStatusPausing) {
1092 p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
1093 hasRunning = false;
1094 SetModified();
1095 break;
1096 }
1097 }
1098 }
1099}
1100
1102{
1103 for (cEvent *p = events.First(); p; p = events.Next(p))
1104 p->SetVersion(0xFF);
1105}
1106
1108{
1109 events.Sort();
1110 // Make sure there are no RunningStatusUndefined before the currently running event:
1111 if (hasRunning) {
1112 for (cEvent *p = events.First(); p; p = events.Next(p)) {
1113 if (p->RunningStatus() >= SI::RunningStatusPausing)
1114 break;
1115 p->SetRunningStatus(SI::RunningStatusNotRunning);
1116 }
1117 }
1118 SetModified();
1119}
1120
1121void cSchedule::DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
1122{
1123 // Events are sorted by start time.
1124 if (SegmentStart > 0 && SegmentEnd > 0) {
1125 cEvent *p = events.First();
1126 while (p) {
1127 cEvent *n = events.Next(p);
1128 if (p->StartTime() >= SegmentStart) {
1129 if (p->StartTime() < SegmentEnd) {
1130 // The event starts within the given time segment.
1131 if ((p->TableID() > 0x4E || TableID == 0x4E) && (p->TableID() != TableID || p->Version() != Version)) {
1132 // The segment overwrites all events from tables with other ids, and
1133 // within the same table id all events must have the same version.
1134 // Special consideration: table 0x4E can only be overwritten with the same id!
1135 DelEvent(p);
1136 }
1137 }
1138 else
1139 break;
1140 }
1141 p = n;
1142 }
1143 }
1144}
1145
1147{
1148 Cleanup(time(NULL));
1149}
1150
1151void cSchedule::Cleanup(time_t Time)
1152{
1153 cEvent *Event;
1154 while ((Event = events.First()) != NULL) {
1155 if (!Event->HasTimer() && Event->EndTime() + EPG_LINGER_TIME < Time)
1156 DelEvent(Event);
1157 else
1158 break;
1159 }
1160}
1161
1162void cSchedule::Dump(const cChannels *Channels, FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime) const
1163{
1164 if (const cChannel *Channel = Channels->GetByChannelID(channelID, true)) {
1165 fprintf(f, "%sC %s %s\n", Prefix, *Channel->GetChannelID().ToString(), Channel->Name());
1166 const cEvent *p;
1167 switch (DumpMode) {
1168 case dmAll: {
1169 for (p = events.First(); p; p = events.Next(p))
1170 p->Dump(f, Prefix);
1171 }
1172 break;
1173 case dmPresent: {
1174 if ((p = GetPresentEvent()) != NULL)
1175 p->Dump(f, Prefix);
1176 }
1177 break;
1178 case dmFollowing: {
1179 if ((p = GetFollowingEvent()) != NULL)
1180 p->Dump(f, Prefix);
1181 }
1182 break;
1183 case dmAtTime: {
1184 if ((p = GetEventAround(AtTime)) != NULL)
1185 p->Dump(f, Prefix);
1186 }
1187 break;
1188 default: esyslog("ERROR: unknown DumpMode %d (%s %d)", DumpMode, __FUNCTION__, __LINE__);
1189 }
1190 fprintf(f, "%sc\n", Prefix);
1191 }
1192}
1193
1194bool cSchedule::Read(FILE *f, cSchedules *Schedules)
1195{
1196 if (Schedules) {
1197 int Line = 0;
1198 cReadLine ReadLine;
1199 char *s;
1200 while ((s = ReadLine.Read(f)) != NULL) {
1201 Line++;
1202 if (*s == 'C') {
1203 s = skipspace(s + 1);
1204 char *p = strchr(s, ' ');
1205 if (p)
1206 *p = 0; // strips optional channel name
1207 if (*s) {
1209 if (channelID.Valid()) {
1210 if (cSchedule *p = Schedules->AddSchedule(channelID)) {
1211 if (!cEvent::Read(f, p, Line))
1212 return false;
1213 p->Sort();
1214 }
1215 }
1216 else {
1217 esyslog("ERROR: invalid channel ID: %s", s);
1218 return false;
1219 }
1220 }
1221 }
1222 else {
1223 esyslog("ERROR: unexpected tag in line %d while reading EPG data: %s", Line, s);
1224 return false;
1225 }
1226 }
1227 return true;
1228 }
1229 return false;
1230}
1231
1232// --- cEpgDataWriter --------------------------------------------------------
1233
1234class cEpgDataWriter : public cThread {
1235private:
1237 bool dump;
1238protected:
1239 virtual void Action(void);
1240public:
1241 cEpgDataWriter(void);
1242 void SetDump(bool Dump) { dump = Dump; }
1243 void Perform(void);
1244 };
1245
1247:cThread("epg data writer", true)
1248{
1249 dump = false;
1250}
1251
1253{
1254 Perform();
1255}
1256
1258{
1259 cMutexLock MutexLock(&mutex); // to make sure fore- and background calls don't cause parellel dumps!
1260 {
1261 cStateKey StateKey;
1262 if (cSchedules *Schedules = cSchedules::GetSchedulesWrite(StateKey, 1000)) {
1263 time_t now = time(NULL);
1264 for (cSchedule *p = Schedules->First(); p; p = Schedules->Next(p))
1265 p->Cleanup(now);
1266 StateKey.Remove();
1267 }
1268 }
1269 if (dump)
1271}
1272
1274
1275// --- cSchedules ------------------------------------------------------------
1276
1278char *cSchedules::epgDataFileName = NULL;
1279time_t cSchedules::lastDump = time(NULL);
1280
1282:cList<cSchedule>("5 Schedules")
1283{
1284}
1285
1286const cSchedules *cSchedules::GetSchedulesRead(cStateKey &StateKey, int TimeoutMs)
1287{
1288 return schedules.Lock(StateKey, false, TimeoutMs) ? &schedules : NULL;
1289}
1290
1292{
1293 return schedules.Lock(StateKey, true, TimeoutMs) ? &schedules : NULL;
1294}
1295
1296void cSchedules::SetEpgDataFileName(const char *FileName)
1297{
1298 free(epgDataFileName);
1299 epgDataFileName = FileName ? strdup(FileName) : NULL;
1301}
1302
1303void cSchedules::Cleanup(bool Force)
1304{
1305 if (Force)
1306 lastDump = 0;
1307 time_t now = time(NULL);
1308 if (now - lastDump > EPGDATAWRITEDELTA) {
1309 if (Force)
1311 else if (!EpgDataWriter.Active())
1313 lastDump = now;
1314 }
1315}
1316
1318{
1320 for (cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->Next(Schedule))
1321 Schedule->ResetVersions();
1322}
1323
1324bool cSchedules::Dump(FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime)
1325{
1326 cSafeFile *sf = NULL;
1327 if (!f) {
1328 sf = new cSafeFile(epgDataFileName);
1329 if (sf->Open())
1330 f = *sf;
1331 else {
1332 LOG_ERROR;
1333 delete sf;
1334 return false;
1335 }
1336 }
1339 for (const cSchedule *p = Schedules->First(); p; p = Schedules->Next(p))
1340 p->Dump(Channels, f, Prefix, DumpMode, AtTime);
1341 if (sf) {
1342 sf->Close();
1343 delete sf;
1344 }
1345 return true;
1346}
1347
1348bool cSchedules::Read(FILE *f)
1349{
1350 bool OwnFile = f == NULL;
1351 if (OwnFile) {
1352 if (epgDataFileName && access(epgDataFileName, R_OK) == 0) {
1353 dsyslog("reading EPG data from %s", epgDataFileName);
1354 if ((f = fopen(epgDataFileName, "r")) == NULL) {
1355 LOG_ERROR;
1356 return false;
1357 }
1358 }
1359 else
1360 return false;
1361 }
1364 bool result = cSchedule::Read(f, Schedules);
1365 if (OwnFile)
1366 fclose(f);
1367 if (result) {
1368 // Initialize the channels' schedule pointers, so that the first WhatsOn menu will come up faster:
1369 for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) {
1370 if (const cSchedule *Schedule = Channel->schedule) {
1371 if (!Schedule->ChannelID().Valid()) // this is the DummySchedule
1372 Channel->schedule = NULL;
1373 }
1374 Schedules->GetSchedule(Channel);
1375 }
1376 }
1377 return result;
1378}
1379
1381{
1382 ChannelID.ClrRid();
1383 cSchedule *p = (cSchedule *)GetSchedule(ChannelID);
1384 if (!p) {
1385 p = new cSchedule(ChannelID);
1386 Add(p);
1387 }
1388 return p;
1389}
1390
1392{
1393 ChannelID.ClrRid();
1394 for (const cSchedule *p = First(); p; p = Next(p)) {
1395 if (p->ChannelID() == ChannelID)
1396 return p;
1397 }
1398 return NULL;
1399}
1400
1401const cSchedule *cSchedules::GetSchedule(const cChannel *Channel, bool AddIfMissing) const
1402{
1403 // This is not very beautiful, but it dramatically speeds up the
1404 // "What's on now/next?" menus.
1405 if (!Channel)
1406 return NULL;
1407 static cSchedule DummySchedule(tChannelID::InvalidID);
1408 if (!Channel->schedule)
1409 Channel->schedule = GetSchedule(Channel->GetChannelID());
1410 if (!Channel->schedule)
1411 Channel->schedule = &DummySchedule;
1412 if (Channel->schedule == &DummySchedule && AddIfMissing) {
1413 cSchedule *Schedule = new cSchedule(Channel->GetChannelID());
1414 ((cSchedules *)this)->Add(Schedule);
1415 Channel->schedule = Schedule;
1416 }
1417 return Channel->schedule != &DummySchedule? Channel->schedule : NULL;
1418}
1419
1420// --- cEpgDataReader --------------------------------------------------------
1421
1423:cThread("epg data reader")
1424{
1425}
1426
1428{
1430}
1431
1432// --- cEpgHandler -----------------------------------------------------------
1433
1435{
1436 EpgHandlers.Add(this);
1437}
1438
1440{
1441 EpgHandlers.Del(this, false);
1442}
1443
1444// --- cEpgHandlers ----------------------------------------------------------
1445
1447
1449{
1450 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1451 if (eh->IgnoreChannel(Channel))
1452 return true;
1453 }
1454 return false;
1455}
1456
1457bool cEpgHandlers::HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version)
1458{
1459 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1460 if (eh->HandleEitEvent(Schedule, EitEvent, TableID, Version))
1461 return true;
1462 }
1463 return false;
1464}
1465
1467{
1468 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1469 if (eh->HandledExternally(Channel))
1470 return true;
1471 }
1472 return false;
1473}
1474
1475bool cEpgHandlers::IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version)
1476{
1477 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1478 if (eh->IsUpdate(EventID, StartTime, TableID, Version))
1479 return true;
1480 }
1481 return false;
1482}
1483
1485{
1486 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1487 if (eh->SetEventID(Event, EventID))
1488 return;
1489 }
1490 Event->SetEventID(EventID);
1491}
1492
1493void cEpgHandlers::SetTitle(cEvent *Event, const char *Title)
1494{
1495 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1496 if (eh->SetTitle(Event, Title))
1497 return;
1498 }
1499 Event->SetTitle(Title);
1500}
1501
1502void cEpgHandlers::SetShortText(cEvent *Event, const char *ShortText)
1503{
1504 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1505 if (eh->SetShortText(Event, ShortText))
1506 return;
1507 }
1508 Event->SetShortText(ShortText);
1509}
1510
1511void cEpgHandlers::SetDescription(cEvent *Event, const char *Description)
1512{
1513 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1514 if (eh->SetDescription(Event, Description))
1515 return;
1516 }
1517 Event->SetDescription(Description);
1518}
1519
1521{
1522 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1523 if (eh->SetContents(Event, Contents))
1524 return;
1525 }
1526 Event->SetContents(Contents);
1527}
1528
1529void cEpgHandlers::SetParentalRating(cEvent *Event, int ParentalRating)
1530{
1531 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1532 if (eh->SetParentalRating(Event, ParentalRating))
1533 return;
1534 }
1535 Event->SetParentalRating(ParentalRating);
1536}
1537
1538void cEpgHandlers::SetStartTime(cEvent *Event, time_t StartTime)
1539{
1540 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1541 if (eh->SetStartTime(Event, StartTime))
1542 return;
1543 }
1544 Event->SetStartTime(StartTime);
1545}
1546
1547void cEpgHandlers::SetDuration(cEvent *Event, int Duration)
1548{
1549 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1550 if (eh->SetDuration(Event, Duration))
1551 return;
1552 }
1553 Event->SetDuration(Duration);
1554}
1555
1556void cEpgHandlers::SetVps(cEvent *Event, time_t Vps)
1557{
1558 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1559 if (eh->SetVps(Event, Vps))
1560 return;
1561 }
1562 Event->SetVps(Vps);
1563}
1564
1566{
1567 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1568 if (eh->SetComponents(Event, Components))
1569 return;
1570 }
1571 Event->SetComponents(Components);
1572}
1573
1575{
1576 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1577 if (eh->FixEpgBugs(Event))
1578 return;
1579 }
1580 Event->FixEpgBugs();
1581}
1582
1584{
1585 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1586 if (eh->HandleEvent(Event))
1587 break;
1588 }
1589}
1590
1592{
1593 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1594 if (eh->SortSchedule(Schedule))
1595 return;
1596 }
1597 Schedule->Sort();
1598}
1599
1600void cEpgHandlers::DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
1601{
1602 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1603 if (eh->DropOutdated(Schedule, SegmentStart, SegmentEnd, TableID, Version))
1604 return;
1605 }
1606 Schedule->DropOutdated(SegmentStart, SegmentEnd, TableID, Version);
1607}
1608
1610{
1611 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1612 if (!eh->BeginSegmentTransfer(Channel, false))
1613 return false;
1614 }
1615 return true;
1616}
1617
1619{
1620 for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
1621 if (eh->EndSegmentTransfer(Modified, false))
1622 return;
1623 }
1624}
#define LOCK_CHANNELS_READ
Definition channels.h:270
#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
tComponent * Component(int Index) const
Definition epg.h:64
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:1427
cEpgDataReader(void)
Definition epg.c:1422
void Perform(void)
Definition epg.c:1257
void SetDump(bool Dump)
Definition epg.c:1242
bool dump
Definition epg.c:1237
cEpgDataWriter(void)
Definition epg.c:1246
cMutex mutex
Definition epg.c:1236
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:1252
cEpgHandler(void)
Constructs a new EPG handler and adds it to the list of EPG handlers.
Definition epg.c:1434
virtual ~cEpgHandler()
Definition epg.c:1439
void SortSchedule(cSchedule *Schedule)
Definition epg.c:1591
void EndSegmentTransfer(bool Modified)
Definition epg.c:1618
bool IgnoreChannel(const cChannel *Channel)
Definition epg.c:1448
bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version)
Definition epg.c:1457
void SetStartTime(cEvent *Event, time_t StartTime)
Definition epg.c:1538
void SetTitle(cEvent *Event, const char *Title)
Definition epg.c:1493
void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
Definition epg.c:1600
bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version)
Definition epg.c:1475
void FixEpgBugs(cEvent *Event)
Definition epg.c:1574
void HandleEvent(cEvent *Event)
Definition epg.c:1583
void SetComponents(cEvent *Event, cComponents *Components)
Definition epg.c:1565
void SetVps(cEvent *Event, time_t Vps)
Definition epg.c:1556
void SetParentalRating(cEvent *Event, int ParentalRating)
Definition epg.c:1529
bool BeginSegmentTransfer(const cChannel *Channel)
Definition epg.c:1609
bool HandledExternally(const cChannel *Channel)
Definition epg.c:1466
void SetContents(cEvent *Event, uchar *Contents)
Definition epg.c:1520
void SetShortText(cEvent *Event, const char *ShortText)
Definition epg.c:1502
void SetDuration(cEvent *Event, int Duration)
Definition epg.c:1547
void SetDescription(cEvent *Event, const char *Description)
Definition epg.c:1511
void SetEventID(cEvent *Event, tEventID EventID)
Definition epg.c:1484
Definition epg.h:73
const char * ShortText(void) const
Definition epg.h:106
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
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
const cComponents * Components(void) const
Definition epg.h:108
uchar Contents(int i=0) const
Definition epg.h:109
const char * Description(void) const
Definition epg.h:107
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
const char * Aux(void) const
Definition epg.h:117
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 char * Title(void) const
Definition epg.h:105
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
const cSchedule * Schedule(void) const
Definition epg.h:100
void SetStartTime(time_t StartTime)
Definition epg.c:216
bool HasTimer(void) const
Definition epg.h:120
void SetComponents(cComponents *Components)
Definition epg.c:199
int duration
Definition epg.h:90
void SetEventID(tEventID EventID)
Definition epg.c:156
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
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
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:2426
void Add(cListObject *Object, unsigned int Id)
Definition tools.c:2418
T * Get(unsigned int Id) const
Definition tools.h:932
void Del(cListObject *Object, bool DeleteObject=true)
Definition tools.c:2251
void SetUseGarbageCollector(void)
Definition tools.h:617
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:2210
void Add(cListObject *Object, cListObject *After=NULL)
Definition tools.c:2219
void Sort(void)
Definition tools.c:2343
Definition tools.h:644
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition tools.h:656
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:663
void Lock(void)
Definition thread.c:222
void Unlock(void)
Definition thread.c:228
char * Read(FILE *f)
Definition tools.c:1512
bool Open(void)
Definition tools.c:1803
bool Close(void)
Definition tools.c:1813
const cEvent * GetPresentEvent(void) const
Definition epg.c:1001
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:1069
void UnhashEvent(cEvent *Event)
Definition epg.c:994
const cEvent * GetEventAround(time_t Time) const
Definition epg.c:1055
const cEvent * GetEventByTime(time_t StartTime) const
Definition epg.c:1048
void DecNumTimers(void) const
Definition epg.c:944
const cEvent * GetEvent(tEventID EventID, time_t StartTime=0) const
Definition epg.c:1032
bool OnActualTp(uchar TableId)
Definition epg.c:951
void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
Definition epg.c:1121
static bool Read(FILE *f, cSchedules *Schedules)
Definition epg.c:1194
cSchedule(tChannelID ChannelID)
Definition epg.c:926
void SetPresentSeen(void)
Definition epg.h:172
static cMutex numTimersMutex
Definition epg.h:154
void ClrRunningStatus(cChannel *Channel=NULL)
Definition epg.c:1087
void ResetVersions(void)
Definition epg.c:1101
cList< cEvent > events
Definition epg.h:156
void Cleanup(void)
Definition epg.c:1146
tChannelID channelID
Definition epg.h:155
const cEvent * GetEventById(tEventID EventID) const
Definition epg.c:1043
void DelEvent(cEvent *Event)
Definition epg.c:966
void HashEvent(cEvent *Event)
Definition epg.c:982
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:1107
bool onActualTp
Definition epg.h:161
cHash< cEvent > eventsHashStartTime
Definition epg.h:158
void IncNumTimers(void) const
Definition epg.c:937
time_t presentSeen
Definition epg.h:163
cEvent * AddEvent(cEvent *Event)
Definition epg.c:958
void Dump(const cChannels *Channels, FILE *f, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0) const
Definition epg.c:1162
bool hasRunning
Definition epg.h:160
const cEvent * GetFollowingEvent(void) const
Definition epg.c:1016
cSchedules(void)
Definition epg.c:1281
static cSchedules * GetSchedulesWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of schedules for write access.
Definition epg.c:1291
const cSchedule * GetSchedule(tChannelID ChannelID) const
Definition epg.c:1391
static const cSchedules * GetSchedulesRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of schedules for read access.
Definition epg.c:1286
static bool Dump(FILE *f=NULL, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0)
Definition epg.c:1324
static void SetEpgDataFileName(const char *FileName)
Definition epg.c:1296
static void Cleanup(bool Force=false)
Definition epg.c:1303
static time_t lastDump
Definition epg.h:206
static char * epgDataFileName
Definition epg.h:205
static void ResetVersions(void)
Definition epg.c:1317
cSchedule * AddSchedule(tChannelID ChannelID)
Definition epg.c:1380
static bool Read(FILE *f=NULL)
Definition epg.c:1348
static cSchedules schedules
Definition epg.h:204
friend class cSchedule
Definition epg.h:202
int EPGBugfixLevel
Definition config.h:301
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
Definition thread.c:867
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1180
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:1446
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:1273
@ MaxEventContents
Definition epg.h:25
cEpgHandlers EpgHandlers
Definition epg.c:1446
#define LOCK_SCHEDULES_READ
Definition epg.h:233
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: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
@ 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
bool Valid(void) const
Definition channels.h:58
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:590
tEpgBugFixStats(void)
Definition epg.c:591
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:1286
bool isempty(const char *s)
Definition tools.c:354
char * strreplace(char *s, char c1, char c2)
Definition tools.c:139
char * compactspace(char *s)
Definition tools.c:236
cString DateString(time_t t)
Converts the given time to a string of the form "www dd.mm.yyyy".
Definition tools.c:1266
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:816
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