premiereepg.c
author nathan
Sat, 29 Dec 2007 11:20:04 +0100
branchtrunk
changeset 12 5213337a6614
parent 10 967afc97e51d
child 14 feccb11658b1
permissions -rw-r--r--
release 0.0.7
     1 /*
     2  * PremiereEpg plugin to VDR (C++)
     3  *
     4  * (C) 2005-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
     5  *
     6  * This code is base on the commandline tool premiereepg2vdr
     7  * (C) 2004-2005 by Axel Katzur software@katzur.de
     8  * but has been rewritten from scratch
     9  *
    10  * This code is free software; you can redistribute it and/or
    11  * modify it under the terms of the GNU General Public License
    12  * as published by the Free Software Foundation; either version 2
    13  * of the License, or (at your option) any later version.
    14  *
    15  * This code is distributed in the hope that it will be useful,
    16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    18  * GNU General Public License for more details.
    19  *
    20  * You should have received a copy of the GNU General Public License
    21  * along with this program; if not, write to the Free Software
    22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    23  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    24  */
    25 
    26 #include <vdr/plugin.h>
    27 #include <vdr/filter.h>
    28 #include <vdr/epg.h>
    29 #include <vdr/channels.h>
    30 #include <vdr/dvbdevice.h>
    31 #include <vdr/i18n.h>
    32 #include <vdr/config.h>
    33 #include <libsi/section.h>
    34 #include <libsi/descriptor.h>
    35 
    36 //#define DEBUG
    37 //#define DEBUG2
    38 
    39 #ifdef DEBUG
    40 #define d(x) { (x); }
    41 #else
    42 #define d(x) ; 
    43 #endif
    44 #ifdef DEBUG2
    45 #define d2(x) { (x); }
    46 #else
    47 #define d2(x) ; 
    48 #endif
    49 
    50 #define PMT_SCAN_TIMEOUT  10  // seconds
    51 #define PMT_SCAN_IDLE     300 // seconds
    52 
    53 static const char *VERSION        = "0.0.7";
    54 static const char *DESCRIPTION    = "Parses extended Premiere EPG data";
    55 
    56 #if APIVERSNUM < 10401
    57 #error You need at least VDR API version 1.4.1 for this plugin
    58 #endif
    59 
    60 // --- cSetupPremiereEpg -------------------------------------------------------
    61 
    62 const char *optPats[] = {
    63   "%s",
    64   "%s (Option %d)",
    65   "%s (O%d)",
    66   "#%2$d %1$s",
    67   "[%2$d] %1$s"
    68   };
    69 #define NUM_PATS (sizeof(optPats)/sizeof(char *))
    70 
    71 class cSetupPremiereEpg {
    72 public:
    73   int OptPat;
    74   int OrderInfo;
    75   int RatingInfo;
    76   int FixEpg;
    77 public:
    78   cSetupPremiereEpg(void);
    79   };
    80 
    81 cSetupPremiereEpg SetupPE;
    82 
    83 cSetupPremiereEpg::cSetupPremiereEpg(void)
    84 {
    85   OptPat=1;
    86   OrderInfo=1;
    87   RatingInfo=1;
    88   FixEpg=0;
    89 }
    90 
    91 // --- i18n --------------------------------------------------------------------
    92 
    93 const tI18nPhrase Phrases[] = {
    94 /*
    95 */
    96   { "PremiereEPG",
    97     "PremiereEPG",
    98     "", // TODO
    99     "", // TODO
   100     "", // TODO
   101     "", // TODO
   102     "", // TODO
   103     "", // TODO
   104     "", // TODO
   105     "", // TODO
   106     "", // TODO
   107     "", // TODO
   108     "", // TODO
   109     "", // TODO
   110     "", // TODO
   111     "", // TODO
   112   },
   113   { "Parses extended Premiere EPG data",
   114     "Liest erweiterte Premiere EPG Daten ein",
   115     "", // TODO
   116     "", // TODO
   117     "", // TODO
   118     "", // TODO
   119     "", // TODO
   120     "", // TODO
   121     "", // TODO
   122     "", // TODO
   123     "", // TODO
   124     "", // TODO
   125     "", // TODO
   126     "", // TODO
   127     "", // TODO
   128     "", // TODO
   129   },
   130   { "Tag option events",
   131     "Options Events markieren",
   132     "", // TODO
   133     "", // TODO
   134     "", // TODO
   135     "", // TODO
   136     "", // TODO
   137     "", // TODO
   138     "", // TODO
   139     "", // TODO
   140     "", // TODO
   141     "", // TODO
   142     "", // TODO
   143     "", // TODO
   144     "", // TODO
   145     "", // TODO
   146   },
   147   { "Show order information",
   148     "Bestellhinweise anzeigen",
   149     "", // TODO
   150     "", // TODO
   151     "", // TODO
   152     "", // TODO
   153     "", // TODO
   154     "", // TODO
   155     "", // TODO
   156     "", // TODO
   157     "", // TODO
   158     "", // TODO
   159     "", // TODO
   160     "", // TODO
   161     "", // TODO
   162     "", // TODO
   163   },
   164   { "Show rating information",
   165     "Altersfreigaben anzeigen",
   166     "", // TODO
   167     "", // TODO
   168     "", // TODO
   169     "", // TODO
   170     "", // TODO
   171     "", // TODO
   172     "", // TODO
   173     "", // TODO
   174     "", // TODO
   175     "", // TODO
   176     "", // TODO
   177     "", // TODO
   178     "", // TODO
   179     "", // TODO
   180   },
   181 
   182   { "Ordernumber",
   183     "Bestellnummer",
   184     "", // TODO
   185     "", // TODO
   186     "", // TODO
   187     "", // TODO
   188     "", // TODO
   189     "", // TODO
   190     "", // TODO
   191     "", // TODO
   192     "", // TODO
   193     "", // TODO
   194     "", // TODO
   195     "", // TODO
   196     "", // TODO
   197     "", // TODO
   198   },
   199   { "Price",
   200     "Preis",
   201     "", // TODO
   202     "", // TODO
   203     "", // TODO
   204     "", // TODO
   205     "", // TODO
   206     "", // TODO
   207     "", // TODO
   208     "", // TODO
   209     "", // TODO
   210     "", // TODO
   211     "", // TODO
   212     "", // TODO
   213     "", // TODO
   214     "", // TODO
   215   },
   216   { "Ordering",
   217     "Bestellen",
   218     "", // TODO
   219     "", // TODO
   220     "", // TODO
   221     "", // TODO
   222     "", // TODO
   223     "", // TODO
   224     "", // TODO
   225     "", // TODO
   226     "", // TODO
   227     "", // TODO
   228     "", // TODO
   229     "", // TODO
   230     "", // TODO
   231     "", // TODO
   232   },
   233   { "SMS",
   234     "SMS",
   235     "", // TODO
   236     "", // TODO
   237     "", // TODO
   238     "", // TODO
   239     "", // TODO
   240     "", // TODO
   241     "", // TODO
   242     "", // TODO
   243     "", // TODO
   244     "", // TODO
   245     "", // TODO
   246     "", // TODO
   247     "", // TODO
   248     "", // TODO
   249   },
   250   { "WWW",
   251     "WWW",
   252     "", // TODO
   253     "", // TODO
   254     "", // TODO
   255     "", // TODO
   256     "", // TODO
   257     "", // TODO
   258     "", // TODO
   259     "", // TODO
   260     "", // TODO
   261     "", // TODO
   262     "", // TODO
   263     "", // TODO
   264     "", // TODO
   265     "", // TODO
   266   },
   267   { "Rating",
   268     "Altersfreigabe",
   269     "", // TODO
   270     "", // TODO
   271     "", // TODO
   272     "", // TODO
   273     "", // TODO
   274     "", // TODO
   275     "", // TODO
   276     "", // TODO
   277     "", // TODO
   278     "", // TODO
   279     "", // TODO
   280     "", // TODO
   281     "", // TODO
   282     "", // TODO
   283   },
   284   { "years",
   285     "Jahre",
   286     "", // TODO
   287     "", // TODO
   288     "", // TODO
   289     "", // TODO
   290     "", // TODO
   291     "", // TODO
   292     "", // TODO
   293     "", // TODO
   294     "", // TODO
   295     "", // TODO
   296     "", // TODO
   297     "", // TODO
   298     "", // TODO
   299     "", // TODO
   300   },
   301   { "Fix EPG data",
   302     "EPG Daten korrigieren",
   303     "", // TODO
   304     "", // TODO
   305     "", // TODO
   306     "", // TODO
   307     "", // TODO
   308     "", // TODO
   309     "", // TODO
   310     "", // TODO
   311     "", // TODO
   312     "", // TODO
   313     "", // TODO
   314     "", // TODO
   315     "", // TODO
   316     "", // TODO
   317   },
   318 
   319   { NULL }
   320   };
   321 
   322 // --- cMenuSetupPremiereEpg ------------------------------------------------------------
   323 
   324 class cMenuSetupPremiereEpg : public cMenuSetupPage {
   325 private:
   326   cSetupPremiereEpg data;
   327   const char *optDisp[NUM_PATS];
   328   char buff[NUM_PATS][32];
   329 protected:
   330   virtual void Store(void);
   331 public:
   332   cMenuSetupPremiereEpg(void);
   333   };
   334 
   335 cMenuSetupPremiereEpg::cMenuSetupPremiereEpg(void)
   336 {
   337   data=SetupPE;
   338   SetSection(tr("PremiereEPG"));
   339   optDisp[0]=tr("off");
   340   for(unsigned int i=1; i<NUM_PATS; i++) {
   341     snprintf(buff[i],sizeof(buff[i]),optPats[i],"Event",1);
   342     optDisp[i]=buff[i];
   343     }
   344   Add(new cMenuEditStraItem(tr("Tag option events"),&data.OptPat,NUM_PATS,optDisp));
   345   Add(new cMenuEditBoolItem(tr("Show order information"),&data.OrderInfo));
   346   Add(new cMenuEditBoolItem(tr("Show rating information"),&data.RatingInfo));
   347   Add(new cMenuEditBoolItem(tr("Fix EPG data"),&data.FixEpg));
   348 }
   349 
   350 void cMenuSetupPremiereEpg::Store(void)
   351 {
   352   SetupPE=data;
   353   SetupStore("OptionPattern",SetupPE.OptPat);
   354   SetupStore("OrderInfo",SetupPE.OrderInfo);
   355   SetupStore("RatingInfo",SetupPE.RatingInfo);
   356   SetupStore("FixEpg",SetupPE.FixEpg);
   357 }
   358 
   359 // --- CRC16 -------------------------------------------------------------------
   360 
   361 #define POLY 0xA001 // CRC16
   362 
   363 unsigned int crc16(unsigned int crc, unsigned char const *p, int len)
   364 {
   365   while(len--) {
   366     crc^=*p++;
   367     for(int i=0; i<8; i++)
   368       crc=(crc&1) ? (crc>>1)^POLY : (crc>>1);
   369     }
   370   return crc&0xFFFF;
   371 }
   372 
   373 // --- cFilterPremiereEpg ------------------------------------------------------
   374 
   375 #define STARTTIME_BIAS (20*60)
   376 
   377 class cFilterPremiereEpg : public cFilter {
   378 private:
   379   int pmtpid, pmtsid, pmtidx, pmtnext;
   380   //
   381   void NextPmt(void);
   382 protected:
   383   virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
   384 public:
   385   cFilterPremiereEpg(void);
   386   virtual void SetStatus(bool On);
   387   void Trigger(void);
   388   };
   389 
   390 cFilterPremiereEpg::cFilterPremiereEpg(void)
   391 {
   392   Trigger();
   393   Set(0x00,0x00);
   394 }
   395 
   396 void cFilterPremiereEpg::Trigger(void)
   397 {
   398   d(printf("trigger\n"))
   399   pmtpid=0; pmtidx=0; pmtnext=0;
   400 }
   401 
   402 void cFilterPremiereEpg::SetStatus(bool On)
   403 {
   404   d(printf("setstatus %d\n",On))
   405   cFilter::SetStatus(On);
   406   Trigger();
   407 }
   408 
   409 void cFilterPremiereEpg::NextPmt(void)
   410 {
   411   Del(pmtpid,0x02);
   412   pmtpid=0;
   413   pmtidx++;
   414   d(printf("PMT next\n"))
   415 }
   416 
   417 void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
   418 {
   419   int now=time(0);
   420   if(Pid==0 && Tid==SI::TableIdPAT) {
   421     if(!pmtnext || now>pmtnext) {
   422       if(pmtpid) NextPmt();
   423       if(!pmtpid) {
   424         SI::PAT pat(Data,false);
   425         if(pat.CheckCRCAndParse()) {
   426           SI::PAT::Association assoc;
   427           int idx=0;
   428           for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
   429             if(!assoc.isNITPid()) {
   430               if(idx++==pmtidx) {
   431                 pmtpid=assoc.getPid();
   432                 pmtsid=assoc.getServiceId();
   433                 Add(pmtpid,0x02);
   434                 pmtnext=now+PMT_SCAN_TIMEOUT;
   435                 d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx))
   436                 break;
   437                 }
   438               }
   439             }
   440           if(!pmtpid) {
   441             pmtidx=0;
   442             pmtnext=now+PMT_SCAN_IDLE;
   443             d(printf("PMT scan idle\n"))
   444             }
   445           }
   446         }
   447       }
   448     }
   449   else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) {
   450     SI::PMT pmt(Data,false);
   451     if(pmt.CheckCRCAndParse() && pmt.getServiceId()==pmtsid) {
   452       SI::PMT::Stream stream;
   453       for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
   454         if(stream.getStreamType()==0x05) {
   455           SI::CharArray data=stream.getData();
   456           if((data[1]&0xE0)==0xE0 && (data[3]&0xF0)==0xF0) {
   457             bool prvData=false, usrData=false;
   458             SI::Descriptor *d;
   459             for(SI::Loop::Iterator it; (d=stream.streamDescriptors.getNext(it)); ) {
   460               switch(d->getDescriptorTag()) {
   461                 case SI::PrivateDataSpecifierDescriptorTag:
   462                   d(printf("prv: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   463                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x000000be)
   464                     prvData=true;
   465                   break;
   466                 case 0x90:
   467                   d(printf("usr: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   468                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x0000ffff)
   469                     usrData=true;
   470                   break;
   471                 default:
   472                   break;
   473                 }
   474               delete d;
   475               }
   476             if(prvData && usrData) {
   477               int pid=stream.getPid();
   478               d(printf("found citpid 0x%04x",pid))
   479               if(!Matches(pid,0xA0)) {
   480                 Add(pid,0xA0);
   481                 d(printf(" (added)"))
   482                 }
   483               d(printf("\n"))
   484               }
   485             }
   486           }
   487         }
   488       NextPmt(); pmtnext=0;
   489       }
   490     }
   491   else if(Tid==0xA0 && Source()) {
   492     SI::PremiereCIT cit(Data,false);
   493     if(cit.CheckCRCAndParse()) {
   494       cSchedulesLock SchedulesLock(true,10);
   495       cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock);
   496       if(Schedules) {
   497         int nCount=0;
   498         SI::ExtendedEventDescriptors *ExtendedEventDescriptors=0;
   499         SI::ShortEventDescriptor *ShortEventDescriptor=0;
   500         char *order=0, *rating=0;
   501         {
   502         time_t firstTime=0;
   503         SI::Descriptor *d;
   504         bool UseExtendedEventDescriptor=false;
   505         int LanguagePreferenceShort=-1;
   506         int LanguagePreferenceExt=-1;
   507         for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
   508           switch(d->getDescriptorTag()) {
   509             case 0xF0: // order information
   510               if(SetupPE.OrderInfo) {
   511                 static const char *text[] = {
   512                   "Ordernumber",
   513                   "Price",
   514                   "Ordering",
   515                   "SMS",
   516                   "WWW"
   517                   };
   518                 char buff[512];
   519                 int p=0;
   520                 const unsigned char *data=d->getData().getData()+2;
   521                 for(int i=0; i<5; i++) {
   522                   int l=data[0]; 
   523                   if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %.*s",tr(text[i]),l,&data[1]);
   524                   data+=l+1;
   525                   }
   526                 if(p>0) order=strdup(buff);
   527                 }
   528               break;
   529             case 0xF1: // parental rating
   530               if(SetupPE.RatingInfo) {
   531                 char buff[512];
   532                 int p=0;
   533                 const unsigned char *data=d->getData().getData()+2;
   534                 p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %d %s",tr("Rating"),data[0]+3,tr("years"));
   535                 data+=7;
   536                 int l=data[0]; 
   537                 if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p," (%.*s)",l,&data[1]);
   538                 if(p>0) rating=strdup(buff);
   539                 }
   540               break;
   541             case SI::PremiereContentTransmissionDescriptorTag:
   542               if(nCount>=0) {
   543                 SI::PremiereContentTransmissionDescriptor *pct=(SI::PremiereContentTransmissionDescriptor *)d;
   544                 nCount++;
   545                 SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
   546                 SI::Loop::Iterator it;
   547                 if(pct->startDayLoop.getNext(sd,it)) {
   548                   SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
   549                   SI::Loop::Iterator it2;
   550                   if(sd.startTimeLoop.getNext(st,it2)) {
   551                     time_t StartTime=st.getStartTime(sd.getMJD());
   552                     if(nCount==1) firstTime=StartTime;
   553                     else if(firstTime<StartTime-5*50 || firstTime>StartTime+5*60)
   554                       nCount=-1;
   555                     }
   556                   }
   557                 }
   558               break;
   559             case SI::ExtendedEventDescriptorTag:
   560               {
   561               SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d;
   562               if(I18nIsPreferredLanguage(Setup.EPGLanguages,eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
   563                  delete ExtendedEventDescriptors;
   564                  ExtendedEventDescriptors=new SI::ExtendedEventDescriptors;
   565                  UseExtendedEventDescriptor=true;
   566                  }
   567               if(UseExtendedEventDescriptor) {
   568                  ExtendedEventDescriptors->Add(eed);
   569                  d=NULL; // so that it is not deleted
   570                  }
   571               if(eed->getDescriptorNumber()==eed->getLastDescriptorNumber())
   572                  UseExtendedEventDescriptor=false;
   573               }
   574               break;
   575             case SI::ShortEventDescriptorTag:
   576               {
   577               SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d;
   578               if(I18nIsPreferredLanguage(Setup.EPGLanguages,sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
   579                  delete ShortEventDescriptor;
   580                  ShortEventDescriptor=sed;
   581                  d=NULL; // so that it is not deleted
   582                  }
   583               }
   584               break;
   585             default:
   586               break;
   587             }
   588           delete d;
   589           }
   590         }
   591 
   592         {
   593         bool Modified=false;
   594         int optCount=0;
   595         unsigned int crc[3];
   596         crc[0]=cit.getContentId();
   597         SI::PremiereContentTransmissionDescriptor *pct;
   598         for(SI::Loop::Iterator it; (pct=(SI::PremiereContentTransmissionDescriptor *)cit.eventDescriptors.getNext(it,SI::PremiereContentTransmissionDescriptorTag)); ) {
   599           int nid=pct->getOriginalNetworkId();
   600           int tid=pct->getTransportStreamId();
   601           int sid=pct->getServiceId();
   602           if(SetupPE.FixEpg) {
   603             if(nid==133) {
   604 	      if     (tid==0x03 && sid==0xf0) { tid=0x02; sid=0xe0; }
   605 	      else if(tid==0x03 && sid==0xf1) { tid=0x02; sid=0xe1; }
   606 	      else if(tid==0x03 && sid==0xf5) { tid=0x03; sid=0xdc; }
   607 	      else if(tid==0x04 && sid==0xd2) { tid=0x11; sid=0xe2; }
   608 	      else if(tid==0x11 && sid==0xd3) { tid=0x11; sid=0xe3; }
   609               }
   610             }
   611           tChannelID channelID(Source(),nid,tid,sid);
   612           cChannel *channel=Channels.GetByChannelID(channelID,true);
   613           if(!channel) continue;
   614 
   615           cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
   616           if(!pSchedule) {
   617              pSchedule=new cSchedule(channelID);
   618              Schedules->Add(pSchedule);
   619              }
   620 
   621           optCount++;
   622           SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
   623           int index=0;
   624           for(SI::Loop::Iterator it; pct->startDayLoop.getNext(sd,it); ) {
   625             int mjd=sd.getMJD();
   626             SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
   627             for(SI::Loop::Iterator it2; sd.startTimeLoop.getNext(st,it2); ) {
   628               time_t StartTime=st.getStartTime(mjd);
   629               time_t EndTime=StartTime+cit.getDuration();
   630               int runningStatus=(StartTime<now && now<EndTime) ? SI::RunningStatusRunning : ((StartTime-30<now && now<StartTime) ? SI::RunningStatusStartsInAFewSeconds : SI::RunningStatusNotRunning);
   631               bool isOpt=false;
   632               if(index++==0 && nCount>1) isOpt=true;
   633               crc[1]=isOpt ? optCount : 0;
   634               crc[2]=StartTime / STARTTIME_BIAS;
   635               tEventID EventId=((('P'<<8)|'W')<<16) | crc16(0,(unsigned char *)crc,sizeof(crc));
   636 
   637               d2(printf("%s R%d %04x/%.4x %d %d/%d %s +%d ",*channelID.ToString(),runningStatus,EventId&0xFFFF,cit.getContentId(),index,isOpt,optCount,stripspace(ctime(&StartTime)),(int)cit.getDuration()/60))
   638               if(EndTime+Setup.EPGLinger*60<now) {
   639                 d2(printf("(old)\n"))
   640                 continue;
   641                 }
   642 
   643               bool newEvent=false;
   644               cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,-1);
   645               if(!pEvent) {
   646                 d2(printf("(new)\n"))
   647                 pEvent=new cEvent(EventId);
   648                 if(!pEvent) continue;
   649                 newEvent=true;
   650                 }
   651               else {
   652                 d2(printf("(upd)\n"))
   653                 pEvent->SetSeen();
   654                 if(pEvent->TableID()==0x00 || pEvent->Version()==cit.getVersionNumber()) {
   655                   if(pEvent->RunningStatus()!=runningStatus)
   656                     pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
   657                   continue;
   658                   }
   659                 }
   660               pEvent->SetEventID(EventId);
   661               pEvent->SetTableID(Tid);
   662               pEvent->SetVersion(cit.getVersionNumber());
   663               pEvent->SetStartTime(StartTime);
   664               pEvent->SetDuration(cit.getDuration());
   665 
   666               if(ShortEventDescriptor) {
   667                 char buffer[256];
   668                 ShortEventDescriptor->name.getText(buffer,sizeof(buffer));
   669                 if(isOpt) {
   670                   char buffer2[sizeof(buffer)+32];
   671                   snprintf(buffer2,sizeof(buffer2),optPats[SetupPE.OptPat],buffer,optCount);
   672                   pEvent->SetTitle(buffer2);
   673                   }
   674                 else
   675                   pEvent->SetTitle(buffer);
   676                 d2(printf("title: %s\n",pEvent->Title()))
   677                 pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer)));
   678                 }
   679               if(ExtendedEventDescriptors) {
   680                 char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ")+1];
   681                 pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer,sizeof(buffer),": "));
   682                 }
   683               if(order || rating) {
   684                 int len=(pEvent->Description() ? strlen(pEvent->Description()) : 0) +
   685                         (order                 ? strlen(order) : 0) +
   686                         (rating                ? strlen(rating) : 0);
   687                 char buffer[len+32];
   688                 buffer[0]=0;
   689                 if(pEvent->Description()) strcat(buffer,pEvent->Description());
   690                 if(rating)                strcat(buffer,rating);
   691                 if(order)                 strcat(buffer,order);
   692                 pEvent->SetDescription(buffer);
   693                 }
   694 
   695               if(newEvent) pSchedule->AddEvent(pEvent);
   696               pEvent->SetComponents(NULL);
   697               pEvent->FixEpgBugs();
   698               if(pEvent->RunningStatus()!=runningStatus)
   699                 pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
   700               pSchedule->DropOutdated(StartTime,EndTime,Tid,cit.getVersionNumber());
   701               Modified=true;
   702               }
   703             }
   704           if(Modified) {
   705             pSchedule->Sort();
   706             Schedules->SetModified(pSchedule);
   707             }
   708           delete pct;
   709           }
   710         }
   711         delete ExtendedEventDescriptors;
   712         delete ShortEventDescriptor;
   713         free(order);
   714         free(rating);
   715         }
   716       }
   717     }
   718 }
   719 
   720 // --- cPluginPremiereEpg ------------------------------------------------------
   721 
   722 class cPluginPremiereEpg : public cPlugin {
   723 private:
   724   struct {
   725     cFilterPremiereEpg *filter;
   726     cDevice *device;
   727     } epg[MAXDVBDEVICES];
   728 public:
   729   cPluginPremiereEpg(void);
   730   virtual const char *Version(void) { return VERSION; }
   731   virtual const char *Description(void) { return tr(DESCRIPTION); }
   732   virtual bool Start(void);
   733   virtual void Stop(void);
   734   virtual cMenuSetupPage *SetupMenu(void);
   735   virtual bool SetupParse(const char *Name, const char *Value);
   736   };
   737 
   738 cPluginPremiereEpg::cPluginPremiereEpg(void)
   739 {
   740   memset(epg,0,sizeof(epg));
   741 }
   742 
   743 bool cPluginPremiereEpg::Start(void)
   744 {
   745   RegisterI18n(Phrases);
   746   for(int i=0; i<MAXDVBDEVICES; i++) {
   747     cDevice *dev=cDevice::GetDevice(i);
   748     if(dev) {
   749       epg[i].device=dev;
   750       dev->AttachFilter(epg[i].filter=new cFilterPremiereEpg);
   751       isyslog("Attached premiere EPG filter to device %d",i);
   752       }
   753     }
   754   return true;
   755 }
   756 
   757 void cPluginPremiereEpg::Stop(void)
   758 {
   759   for(int i=0; i<MAXDVBDEVICES; i++) {
   760     cDevice *dev=epg[i].device;
   761     if(dev) dev->Detach(epg[i].filter);
   762     delete epg[i].filter;
   763     epg[i].device=0;
   764     epg[i].filter=0;
   765     }
   766 }
   767 
   768 cMenuSetupPage *cPluginPremiereEpg::SetupMenu(void)
   769 {
   770   return new cMenuSetupPremiereEpg;
   771 }
   772 
   773 bool cPluginPremiereEpg::SetupParse(const char *Name, const char *Value)
   774 {
   775   if      (!strcasecmp(Name, "OptionPattern")) SetupPE.OptPat     = atoi(Value);
   776   else if (!strcasecmp(Name, "OrderInfo"))     SetupPE.OrderInfo  = atoi(Value);
   777   else if (!strcasecmp(Name, "RatingInfo"))    SetupPE.RatingInfo = atoi(Value);
   778   else if (!strcasecmp(Name, "FixEpg"))        SetupPE.FixEpg     = atoi(Value);
   779   else return false;
   780   return true;
   781 }
   782 
   783 VDRPLUGINCREATOR(cPluginPremiereEpg); // Don't touch this!