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