summaryrefslogtreecommitdiff
path: root/epghandler.c
diff options
context:
space:
mode:
authorMatti Lehtimäki <matti.lehtimaki@gmail.com>2012-04-12 19:25:00 +0300
committerMatti Lehtimäki <matti.lehtimaki@gmail.com>2012-04-12 19:25:00 +0300
commitc31263388a5dbdc5150595b328d50fa486b4dce5 (patch)
tree130d8ba9d3f3eb9b29d3070024e4f95ef285ed31 /epghandler.c
downloadvdr-plugin-epgfixer-c31263388a5dbdc5150595b328d50fa486b4dce5.tar.gz
vdr-plugin-epgfixer-c31263388a5dbdc5150595b328d50fa486b4dce5.tar.bz2
Import version 0.0.4 to git.
Diffstat (limited to 'epghandler.c')
-rw-r--r--epghandler.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/epghandler.c b/epghandler.c
new file mode 100644
index 0000000..cf84838
--- /dev/null
+++ b/epghandler.c
@@ -0,0 +1,367 @@
+/*
+ * epghandler.c: EpgHandler
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "epghandler.h"
+#include "config.h"
+#include <vdr/tools.h>
+#include <string.h>
+
+#ifdef HAVE_PCREPOSIX
+#include <pcre.h>
+#endif
+
+typedef enum { ATITLE,PTITLE,TITLE,ASHORTTEXT,PSHORTTEXT,SHORTTEXT,ADESCRIPTION,PDESCRIPTION,DESCRIPTION,RATING } backrefs;
+const char *strBackrefs[] = { "atitle","ptitle","title","ashorttext","pshorttext","shorttext","adescription","pdescription","description","rating" };
+
+void cEpgfixerEpgHandler::FixOriginalEpgBugs(cEvent *event)
+{
+ if (isempty(event->Title())) {
+ // we don't want any "(null)" titles
+ event->SetTitle("No title");
+ }
+
+ // Some TV stations apparently have their own idea about how to fill in the
+ // EPG data. Let's fix their bugs as good as we can:
+
+ // Some channels put the ShortText in quotes and use either the ShortText
+ // or the Description field, depending on how long the string is:
+ //
+ // Title
+ // "ShortText". Description
+ //
+ if (EpgfixerSetup.quotedshorttext && (event->ShortText() == NULL) != (event->Description() == NULL)) {
+ char *p = event->ShortText() ? strdup(event->ShortText()) : strdup(event->Description());
+ if (*p == '"') {
+ const char *delim = "\".";
+ char *e = strstr(p + 1, delim);
+ if (e) {
+ *e = 0;
+ char *s = strdup(p + 1);
+ char *d = strdup(e + strlen(delim));
+ event->SetShortText(s);
+ event->SetDescription(d);
+ }
+ }
+ free(p);
+ }
+
+ // Some channels put the Description into the ShortText (preceded
+ // by a blank) if there is no actual ShortText and the Description
+ // is short enough:
+ //
+ // Title
+ // Description
+ //
+ if (EpgfixerSetup.blankbeforedescription && event->ShortText() && !event->Description()) {
+ if (*event->ShortText() == ' ') {
+ char *shortText = strdup(event->ShortText());
+ memmove(shortText, shortText + 1, strlen(shortText));
+ event->SetDescription(shortText);
+ event->SetShortText(NULL);
+ free(shortText);
+ }
+ }
+
+ // Sometimes they repeat the Title in the ShortText:
+ //
+ // Title
+ // Title
+ //
+ if (EpgfixerSetup.repeatedtitle && event->ShortText() && strcmp(event->Title(), event->ShortText()) == 0) {
+ event->SetShortText(NULL);
+ }
+
+ // Some channels put the ShortText between double quotes, which is nothing
+ // but annoying (some even put a '.' after the closing '"'):
+ //
+ // Title
+ // "ShortText"[.]
+ //
+ if (EpgfixerSetup.doublequotedshorttext && event->ShortText() && *event->ShortText() == '"') {
+ char *shortText = strdup(event->ShortText());
+ int l = strlen(shortText);
+ if (l > 2 && (shortText[l - 1] == '"' || (shortText[l - 1] == '.' && shortText[l - 2] == '"'))) {
+ memmove(shortText, shortText + 1, l);
+ char *p = strrchr(shortText, '"');
+ if (p)
+ *p = 0;
+ }
+ event->SetShortText(shortText);
+ free(shortText);
+ }
+
+ // Some channels apparently try to do some formatting in the texts,
+ // which is a bad idea because they have no way of knowing the width
+ // of the window that will actually display the text.
+ // Remove excess whitespace:
+ if (EpgfixerSetup.removeformatting) {
+ char *temp;
+ temp = strdup(event->Title());
+ event->SetTitle(compactspace(temp));
+ free(temp);
+ if (event->ShortText()) {
+ temp = strdup(event->ShortText());
+ event->SetShortText(compactspace(temp));
+ free(temp);
+ }
+ if (event->Description()) {
+ temp = strdup(event->Description());
+ event->SetDescription(compactspace(temp));
+ free(temp);
+ }
+ }
+
+#define MAX_USEFUL_EPISODE_LENGTH 40
+ // Some channels put a whole lot of information in the ShortText and leave
+ // the Description totally empty. So if the ShortText length exceeds
+ // MAX_USEFUL_EPISODE_LENGTH, let's put this into the Description
+ // instead:
+ if (EpgfixerSetup.longshorttext && !isempty(event->ShortText()) && isempty(event->Description())) {
+ if (strlen(event->ShortText()) > MAX_USEFUL_EPISODE_LENGTH) {
+ event->SetDescription(event->ShortText());
+ event->SetShortText(NULL);
+ }
+ }
+
+ // Some channels put the same information into ShortText and Description.
+ // In that case we delete one of them:
+ if (EpgfixerSetup.equalshorttextanddescription && event->ShortText() && event->Description() && strcmp(event->ShortText(), event->Description()) == 0) {
+ if (strlen(event->ShortText()) > MAX_USEFUL_EPISODE_LENGTH) {
+ event->SetShortText(NULL);
+ }
+ else {
+ event->SetDescription(NULL);
+
+ }
+ }
+
+ // Some channels use the ` ("backtick") character, where a ' (single quote)
+ // would be normally used. Actually, "backticks" in normal text don't make
+ // much sense, so let's replace them:
+ if (EpgfixerSetup.nobackticks) {
+ char *temp;
+ temp = strdup(event->Title());
+ event->SetTitle(strreplace(temp, '`', '\''));
+ free(temp);
+ if (event->ShortText()) {
+ temp = strdup(event->ShortText());
+ event->SetShortText(strreplace(temp, '`', '\''));
+ free(temp);
+ }
+ if (event->Description()) {
+ temp = strdup(event->Description());
+ event->SetDescription(strreplace(temp, '`', '\''));
+ free(temp);
+ }
+ }
+
+ // The stream components have a "description" field which some channels
+ // apparently have no idea of how to set correctly:
+ const cComponents *components = event->Components();
+ if (EpgfixerSetup.components && components) {
+ for (int i = 0; i < components->NumComponents(); i++) {
+ tComponent *p = components->Component(i);
+ switch (p->stream) {
+ case 0x01: { // video
+ if (p->description) {
+ if (strcasecmp(p->description, "Video") == 0 ||
+ strcasecmp(p->description, "Bildformat") == 0) {
+ // Yes, we know it's video - that's what the 'stream' code
+ // is for! But _which_ video is it?
+ free(p->description);
+ p->description = NULL;
+ }
+ }
+ if (!p->description) {
+ switch (p->type) {
+ case 0x01:
+ case 0x05: p->description = strdup("4:3"); break;
+ case 0x02:
+ case 0x03:
+ case 0x06:
+ case 0x07: p->description = strdup("16:9"); break;
+ case 0x04:
+ case 0x08: p->description = strdup(">16:9"); break;
+ case 0x09:
+ case 0x0D: p->description = strdup("HD 4:3"); break;
+ case 0x0A:
+ case 0x0B:
+ case 0x0E:
+ case 0x0F: p->description = strdup("HD 16:9"); break;
+ case 0x0C:
+ case 0x10: p->description = strdup("HD >16:9"); break;
+ default: ;
+ }
+ }
+ }
+ break;
+ case 0x02: { // audio
+ if (p->description) {
+ if (strcasecmp(p->description, "Audio") == 0) {
+ // Yes, we know it's audio - that's what the 'stream' code
+ // is for! But _which_ audio is it?
+ free(p->description);
+ p->description = NULL;
+ }
+ }
+ if (!p->description) {
+ switch (p->type) {
+ case 0x05: p->description = strdup("Dolby Digital"); break;
+ default: ; // all others will just display the language
+ }
+ }
+ }
+ break;
+ default: ;
+ }
+ }
+ }
+
+ // VDR can't usefully handle newline characters in the title, shortText or component description of EPG
+ // data, so let's always convert them to blanks (independent of the setting of EPGBugfixLevel):
+ char *temp = strdup(event->Title());
+ event->SetTitle(strreplace(temp, '\n', ' '));
+ free(temp);
+ if (event->ShortText()) {
+ temp = strdup(event->ShortText());
+ event->SetShortText(strreplace(temp, '\n', ' '));
+ free(temp);
+ }
+ if (components) {
+ for (int i = 0; i < components->NumComponents(); i++) {
+ tComponent *p = components->Component(i);
+ if (p->description)
+ strreplace(p->description, '\n', ' ');
+ }
+ }
+}
+
+bool cEpgfixerEpgHandler::ApplyRegexp(cRegexp *regexp, cEvent *Event, const char *input)
+{
+ bool active = true;
+ if (regexp->NumChannels() > 0) {
+ bool found = false;
+ int i = 0;
+ while (i < regexp->NumChannels()) {
+ if (Channels.GetByChannelID(Event->ChannelID())->Number() == regexp->GetChannel(i))
+ found = true;
+ i++;
+ }
+ if (!found) {
+ active = false;
+ }
+ }
+ if (active && regexp->Enabled() && regexp->GetRe()) {
+ const char *string;
+ int ovector[20];
+ int rc;
+ rc = pcre_exec(regexp->GetRe(), regexp->GetSd(), input, strlen(input), 0, 0, ovector, 20);
+ if (rc > 0) {
+ int i = 0;
+ while (i < 10) {
+ if (pcre_get_named_substring(regexp->GetRe(), input, ovector, rc, strBackrefs[i], &string) != PCRE_ERROR_NOSUBSTRING) {
+ char *tempstring = 0;
+ switch (i) {
+ case TITLE:
+ Event->SetTitle(string);
+ break;
+ case ATITLE:
+ tempstring = (char *)malloc(strlen(Event->Title())+strlen(string)+1);
+ strcpy(tempstring, Event->Title());
+ strcat(tempstring, string);
+ Event->SetTitle(tempstring);
+ break;
+ case PTITLE:
+ tempstring = (char *)malloc(strlen(Event->Title())+strlen(string)+1);
+ strcpy(tempstring, string);
+ strcat(tempstring, Event->Title());
+ Event->SetTitle(tempstring);
+ break;
+ case SHORTTEXT:
+ Event->SetShortText(string);
+ break;
+ case ASHORTTEXT:
+ tempstring = (char *)malloc(strlen(Event->ShortText())+strlen(string)+1);
+ strcpy(tempstring, Event->ShortText());
+ strcat(tempstring, string);
+ Event->SetShortText(tempstring);
+ break;
+ case PSHORTTEXT:
+ tempstring = (char *)malloc(strlen(Event->ShortText())+strlen(string)+1);
+ strcpy(tempstring, string);
+ strcat(tempstring, Event->ShortText());
+ Event->SetShortText(tempstring);
+ break;
+ case DESCRIPTION:
+ Event->SetDescription(string);
+ break;
+ case ADESCRIPTION:
+ tempstring = (char *)malloc(strlen(Event->Description())+strlen(string)+1);
+ strcpy(tempstring, Event->Description());
+ strcat(tempstring, string);
+ Event->SetDescription(tempstring);
+ break;
+ case PDESCRIPTION:
+ tempstring = (char *)malloc(strlen(Event->Description())+strlen(string)+1);
+ strcpy(tempstring, string);
+ strcat(tempstring, Event->Description());
+ Event->SetDescription(tempstring);
+ break;
+ case RATING:
+ Event->SetParentalRating(atoi(string));
+ break;
+ default:
+ break;
+ }
+ pcre_free_substring(string);
+ free(tempstring);
+ }
+ i++;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool cEpgfixerEpgHandler::FixBugs(cEvent *Event)
+{
+ int res = false;
+ char *tmpstring;
+ cRegexp *regex = (cRegexp *)EpgfixerRegexps.regexps->First();
+ while (regex) {
+ if (regex->Enabled()) {
+ switch (regex->GetSource()) {
+ case REGEXP_TITLE:
+ tmpstring = strdup(Event->Title());
+ break;
+ case REGEXP_SHORTTEXT:
+ tmpstring = strdup(Event->ShortText());
+ break;
+ case REGEXP_DESCRIPTION:
+ tmpstring = strdup(Event->Description());
+ break;
+ default:
+ tmpstring = strdup("");
+ break;
+ }
+ int ret = ApplyRegexp(regex, Event, tmpstring);
+ if (ret && !res)
+ res = true;
+ free(tmpstring);
+ }
+ regex = (cRegexp *)regex->Next();
+ }
+ return res;
+}
+
+bool cEpgfixerEpgHandler::FixEpgBugs(cEvent *Event)
+{
+ FixOriginalEpgBugs(Event);
+ FixBugs(Event);
+ return false;
+}