premiereepg.c
author nathan
Sat, 29 Dec 2007 11:19:49 +0100
branchtrunk
changeset 10 967afc97e51d
parent 8 b5bd55cfd28f
child 12 5213337a6614
permissions -rw-r--r--
release 0.0.6
     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.6";
    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 public:
    77   cSetupPremiereEpg(void);
    78   };
    79 
    80 cSetupPremiereEpg SetupPE;
    81 
    82 cSetupPremiereEpg::cSetupPremiereEpg(void)
    83 {
    84   OptPat=1;
    85   OrderInfo=1;
    86   RatingInfo=1;
    87 }
    88 
    89 // --- i18n --------------------------------------------------------------------
    90 
    91 const tI18nPhrase Phrases[] = {
    92 /*
    93 */
    94   { "PremiereEPG",
    95     "PremiereEPG",
    96     "", // TODO
    97     "", // TODO
    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   },
   111   { "Parses extended Premiere EPG data",
   112     "Liest erweiterte Premiere EPG Daten ein",
   113     "", // TODO
   114     "", // TODO
   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   },
   128   { "Tag option events",
   129     "Options Events markieren",
   130     "", // TODO
   131     "", // TODO
   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   },
   145   { "Show order information",
   146     "Bestellhinweise anzeigen",
   147     "", // TODO
   148     "", // TODO
   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   },
   162   { "Show rating information",
   163     "Altersfreigaben anzeigen",
   164     "", // TODO
   165     "", // TODO
   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   },
   179 
   180   { "Ordernumber",
   181     "Bestellnummer",
   182     "", // TODO
   183     "", // TODO
   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   },
   197   { "Price",
   198     "Preis",
   199     "", // TODO
   200     "", // TODO
   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   },
   214   { "Ordering",
   215     "Bestellen",
   216     "", // TODO
   217     "", // TODO
   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   },
   231   { "SMS",
   232     "SMS",
   233     "", // TODO
   234     "", // TODO
   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   },
   248   { "WWW",
   249     "WWW",
   250     "", // TODO
   251     "", // TODO
   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   },
   265   { "Rating",
   266     "Altersfreigabe",
   267     "", // TODO
   268     "", // TODO
   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   },
   282   { "years",
   283     "Jahre",
   284     "", // TODO
   285     "", // TODO
   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   },
   299 
   300   { NULL }
   301   };
   302 
   303 // --- cMenuSetupPremiereEpg ------------------------------------------------------------
   304 
   305 class cMenuSetupPremiereEpg : public cMenuSetupPage {
   306 private:
   307   cSetupPremiereEpg data;
   308   const char *optDisp[NUM_PATS];
   309   char buff[NUM_PATS][32];
   310 protected:
   311   virtual void Store(void);
   312 public:
   313   cMenuSetupPremiereEpg(void);
   314   };
   315 
   316 cMenuSetupPremiereEpg::cMenuSetupPremiereEpg(void)
   317 {
   318   data=SetupPE;
   319   SetSection(tr("PremiereEPG"));
   320   optDisp[0]=tr("off");
   321   for(unsigned int i=1; i<NUM_PATS; i++) {
   322     snprintf(buff[i],sizeof(buff[i]),optPats[i],"Event",1);
   323     optDisp[i]=buff[i];
   324     }
   325   Add(new cMenuEditStraItem(tr("Tag option events"),&data.OptPat,NUM_PATS,optDisp));
   326   Add(new cMenuEditBoolItem(tr("Show order information"),&data.OrderInfo));
   327   Add(new cMenuEditBoolItem(tr("Show rating information"),&data.RatingInfo));
   328 }
   329 
   330 void cMenuSetupPremiereEpg::Store(void)
   331 {
   332   SetupPE=data;
   333   SetupStore("OptionPattern",SetupPE.OptPat);
   334   SetupStore("OrderInfo",SetupPE.OrderInfo);
   335   SetupStore("RatingInfo",SetupPE.RatingInfo);
   336 }
   337 
   338 // --- CRC16 -------------------------------------------------------------------
   339 
   340 #define POLY 0xA001 // CRC16
   341 
   342 unsigned int crc16(unsigned int crc, unsigned char const *p, int len)
   343 {
   344   while(len--) {
   345     crc^=*p++;
   346     for(int i=0; i<8; i++)
   347       crc=(crc&1) ? (crc>>1)^POLY : (crc>>1);
   348     }
   349   return crc&0xFFFF;
   350 }
   351 
   352 // --- cFilterPremiereEpg ------------------------------------------------------
   353 
   354 #define STARTTIME_BIAS (20*60)
   355 
   356 class cFilterPremiereEpg : public cFilter {
   357 private:
   358   int pmtpid, pmtsid, pmtidx, pmtnext;
   359   //
   360   void NextPmt(void);
   361 protected:
   362   virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
   363 public:
   364   cFilterPremiereEpg(void);
   365   virtual void SetStatus(bool On);
   366   void Trigger(void);
   367   };
   368 
   369 cFilterPremiereEpg::cFilterPremiereEpg(void)
   370 {
   371   Trigger();
   372   Set(0x00,0x00);
   373 }
   374 
   375 void cFilterPremiereEpg::Trigger(void)
   376 {
   377   d(printf("trigger\n"))
   378   pmtpid=0; pmtidx=0; pmtnext=0;
   379 }
   380 
   381 void cFilterPremiereEpg::SetStatus(bool On)
   382 {
   383   d(printf("setstatus %d\n",On))
   384   cFilter::SetStatus(On);
   385   Trigger();
   386 }
   387 
   388 void cFilterPremiereEpg::NextPmt(void)
   389 {
   390   Del(pmtpid,0x02);
   391   pmtpid=0;
   392   pmtidx++;
   393   d(printf("PMT next\n"))
   394 }
   395 
   396 void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
   397 {
   398   int now=time(0);
   399   if(Pid==0 && Tid==SI::TableIdPAT) {
   400     if(!pmtnext || now>pmtnext) {
   401       if(pmtpid) NextPmt();
   402       if(!pmtpid) {
   403         SI::PAT pat(Data,false);
   404         if(pat.CheckCRCAndParse()) {
   405           SI::PAT::Association assoc;
   406           int idx=0;
   407           for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
   408             if(!assoc.isNITPid()) {
   409               if(idx++==pmtidx) {
   410                 pmtpid=assoc.getPid();
   411                 pmtsid=assoc.getServiceId();
   412                 Add(pmtpid,0x02);
   413                 pmtnext=now+PMT_SCAN_TIMEOUT;
   414                 d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx))
   415                 break;
   416                 }
   417               }
   418             }
   419           if(!pmtpid) {
   420             pmtidx=0;
   421             pmtnext=now+PMT_SCAN_IDLE;
   422             d(printf("PMT scan idle\n"))
   423             }
   424           }
   425         }
   426       }
   427     }
   428   else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) {
   429     SI::PMT pmt(Data,false);
   430     if(pmt.CheckCRCAndParse() && pmt.getServiceId()==pmtsid) {
   431       SI::PMT::Stream stream;
   432       for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
   433         if(stream.getStreamType()==0x05) {
   434           SI::CharArray data=stream.getData();
   435           if((data[1]&0xE0)==0xE0 && (data[3]&0xF0)==0xF0) {
   436             bool prvData=false, usrData=false;
   437             SI::Descriptor *d;
   438             for(SI::Loop::Iterator it; (d=stream.streamDescriptors.getNext(it)); ) {
   439               switch(d->getDescriptorTag()) {
   440                 case SI::PrivateDataSpecifierDescriptorTag:
   441                   d(printf("prv: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   442                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x000000be)
   443                     prvData=true;
   444                   break;
   445                 case 0x90:
   446                   d(printf("usr: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   447                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x0000ffff)
   448                     usrData=true;
   449                   break;
   450                 default:
   451                   break;
   452                 }
   453               delete d;
   454               }
   455             if(prvData && usrData) {
   456               int pid=stream.getPid();
   457               d(printf("found citpid 0x%04x",pid))
   458               if(!Matches(pid,0xA0)) {
   459                 Add(pid,0xA0);
   460                 d(printf(" (added)"))
   461                 }
   462               d(printf("\n"))
   463               }
   464             }
   465           }
   466         }
   467       NextPmt(); pmtnext=0;
   468       }
   469     }
   470   else if(Tid==0xA0 && Source()) {
   471     SI::PremiereCIT cit(Data,false);
   472     if(cit.CheckCRCAndParse()) {
   473       cSchedulesLock SchedulesLock(true,10);
   474       cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock);
   475       if(Schedules) {
   476         int nCount=0;
   477         SI::ExtendedEventDescriptors *ExtendedEventDescriptors=0;
   478         SI::ShortEventDescriptor *ShortEventDescriptor=0;
   479         char *order=0, *rating=0;
   480         {
   481         time_t firstTime=0;
   482         SI::Descriptor *d;
   483         bool UseExtendedEventDescriptor=false;
   484         int LanguagePreferenceShort=-1;
   485         int LanguagePreferenceExt=-1;
   486         for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
   487           switch(d->getDescriptorTag()) {
   488             case 0xF0: // order information
   489               if(SetupPE.OrderInfo) {
   490                 static const char *text[] = {
   491                   "Ordernumber",
   492                   "Price",
   493                   "Ordering",
   494                   "SMS",
   495                   "WWW"
   496                   };
   497                 char buff[512];
   498                 int p=0;
   499                 const unsigned char *data=d->getData().getData()+2;
   500                 for(int i=0; i<5; i++) {
   501                   int l=data[0]; 
   502                   if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %.*s",tr(text[i]),l,&data[1]);
   503                   data+=l+1;
   504                   }
   505                 if(p>0) order=strdup(buff);
   506                 }
   507               break;
   508             case 0xF1: // parental rating
   509               if(SetupPE.RatingInfo) {
   510                 char buff[512];
   511                 int p=0;
   512                 const unsigned char *data=d->getData().getData()+2;
   513                 p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %d %s",tr("Rating"),data[0]+3,tr("years"));
   514                 data+=7;
   515                 int l=data[0]; 
   516                 if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p," (%.*s)",l,&data[1]);
   517                 if(p>0) rating=strdup(buff);
   518                 }
   519               break;
   520             case SI::PremiereContentTransmissionDescriptorTag:
   521               if(nCount>=0) {
   522                 SI::PremiereContentTransmissionDescriptor *pct=(SI::PremiereContentTransmissionDescriptor *)d;
   523                 nCount++;
   524                 SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
   525                 SI::Loop::Iterator it;
   526                 if(pct->startDayLoop.getNext(sd,it)) {
   527                   SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
   528                   SI::Loop::Iterator it2;
   529                   if(sd.startTimeLoop.getNext(st,it2)) {
   530                     time_t StartTime=st.getStartTime(sd.getMJD());
   531                     if(nCount==1) firstTime=StartTime;
   532                     else if(firstTime<StartTime-5*50 || firstTime>StartTime+5*60)
   533                       nCount=-1;
   534                     }
   535                   }
   536                 }
   537               break;
   538             case SI::ExtendedEventDescriptorTag:
   539               {
   540               SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d;
   541               if(I18nIsPreferredLanguage(Setup.EPGLanguages,eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
   542                  delete ExtendedEventDescriptors;
   543                  ExtendedEventDescriptors=new SI::ExtendedEventDescriptors;
   544                  UseExtendedEventDescriptor=true;
   545                  }
   546               if(UseExtendedEventDescriptor) {
   547                  ExtendedEventDescriptors->Add(eed);
   548                  d=NULL; // so that it is not deleted
   549                  }
   550               if(eed->getDescriptorNumber()==eed->getLastDescriptorNumber())
   551                  UseExtendedEventDescriptor=false;
   552               }
   553               break;
   554             case SI::ShortEventDescriptorTag:
   555               {
   556               SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d;
   557               if(I18nIsPreferredLanguage(Setup.EPGLanguages,sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
   558                  delete ShortEventDescriptor;
   559                  ShortEventDescriptor=sed;
   560                  d=NULL; // so that it is not deleted
   561                  }
   562               }
   563               break;
   564             default:
   565               break;
   566             }
   567           delete d;
   568           }
   569         }
   570 
   571         {
   572         bool Modified=false;
   573         int optCount=0;
   574         unsigned int crc[3];
   575         crc[0]=cit.getContentId();
   576         SI::PremiereContentTransmissionDescriptor *pct;
   577         for(SI::Loop::Iterator it; (pct=(SI::PremiereContentTransmissionDescriptor *)cit.eventDescriptors.getNext(it,SI::PremiereContentTransmissionDescriptorTag)); ) {
   578           tChannelID channelID(Source(),pct->getOriginalNetworkId(),pct->getTransportStreamId(),pct->getServiceId());
   579           cChannel *channel=Channels.GetByChannelID(channelID,true);
   580           if(!channel) continue;
   581 
   582           cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
   583           if(!pSchedule) {
   584              pSchedule=new cSchedule(channelID);
   585              Schedules->Add(pSchedule);
   586              }
   587 
   588           optCount++;
   589           SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
   590           int index=0;
   591           for(SI::Loop::Iterator it; pct->startDayLoop.getNext(sd,it); ) {
   592             int mjd=sd.getMJD();
   593             SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
   594             for(SI::Loop::Iterator it2; sd.startTimeLoop.getNext(st,it2); ) {
   595               time_t StartTime=st.getStartTime(mjd);
   596               time_t EndTime=StartTime+cit.getDuration();
   597               int runningStatus=(StartTime<now && now<EndTime) ? SI::RunningStatusRunning : ((StartTime-30<now && now<StartTime) ? SI::RunningStatusStartsInAFewSeconds : SI::RunningStatusNotRunning);
   598               bool isOpt=false;
   599               if(index++==0 && nCount>1) isOpt=true;
   600               crc[1]=isOpt ? optCount : 0;
   601               crc[2]=StartTime / STARTTIME_BIAS;
   602               tEventID EventId=((('P'<<8)|'W')<<16) | crc16(0,(unsigned char *)crc,sizeof(crc));
   603 
   604               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))
   605               if(EndTime+Setup.EPGLinger*60<now) {
   606                 d2(printf("(old)\n"))
   607                 continue;
   608                 }
   609 
   610               bool newEvent=false;
   611               cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,-1);
   612               if(!pEvent) {
   613                 d2(printf("(new)\n"))
   614                 pEvent=new cEvent(EventId);
   615                 if(!pEvent) continue;
   616                 newEvent=true;
   617                 }
   618               else {
   619                 d2(printf("(upd)\n"))
   620                 pEvent->SetSeen();
   621                 if(pEvent->TableID()==0x00 || pEvent->Version()==cit.getVersionNumber()) {
   622                   if(pEvent->RunningStatus()!=runningStatus)
   623                     pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
   624                   continue;
   625                   }
   626                 }
   627               pEvent->SetEventID(EventId);
   628               pEvent->SetTableID(Tid);
   629               pEvent->SetVersion(cit.getVersionNumber());
   630               pEvent->SetStartTime(StartTime);
   631               pEvent->SetDuration(cit.getDuration());
   632 
   633               if(ShortEventDescriptor) {
   634                 char buffer[256];
   635                 ShortEventDescriptor->name.getText(buffer,sizeof(buffer));
   636                 if(isOpt) {
   637                   char buffer2[sizeof(buffer)+32];
   638                   snprintf(buffer2,sizeof(buffer2),optPats[SetupPE.OptPat],buffer,optCount);
   639                   pEvent->SetTitle(buffer2);
   640                   }
   641                 else
   642                   pEvent->SetTitle(buffer);
   643                 d2(printf("title: %s\n",pEvent->Title()))
   644                 pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer)));
   645                 }
   646               if(ExtendedEventDescriptors) {
   647                 char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ")+1];
   648                 pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer,sizeof(buffer),": "));
   649                 }
   650               if(order || rating) {
   651                 int len=(pEvent->Description() ? strlen(pEvent->Description()) : 0) +
   652                         (order                 ? strlen(order) : 0) +
   653                         (rating                ? strlen(rating) : 0);
   654                 char buffer[len+32];
   655                 buffer[0]=0;
   656                 if(pEvent->Description()) strcat(buffer,pEvent->Description());
   657                 if(rating)                strcat(buffer,rating);
   658                 if(order)                 strcat(buffer,order);
   659                 pEvent->SetDescription(buffer);
   660                 }
   661 
   662               if(newEvent) pSchedule->AddEvent(pEvent);
   663               pEvent->SetComponents(NULL);
   664               pEvent->FixEpgBugs();
   665               if(pEvent->RunningStatus()!=runningStatus)
   666                 pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
   667               pSchedule->DropOutdated(StartTime,EndTime,Tid,cit.getVersionNumber());
   668               Modified=true;
   669               }
   670             }
   671           if(Modified) {
   672             pSchedule->Sort();
   673             Schedules->SetModified(pSchedule);
   674             }
   675           delete pct;
   676           }
   677         }
   678         delete ExtendedEventDescriptors;
   679         delete ShortEventDescriptor;
   680         free(order);
   681         free(rating);
   682         }
   683       }
   684     }
   685 }
   686 
   687 // --- cPluginPremiereEpg ------------------------------------------------------
   688 
   689 class cPluginPremiereEpg : public cPlugin {
   690 private:
   691   struct {
   692     cFilterPremiereEpg *filter;
   693     cDevice *device;
   694     } epg[MAXDVBDEVICES];
   695 public:
   696   cPluginPremiereEpg(void);
   697   virtual const char *Version(void) { return VERSION; }
   698   virtual const char *Description(void) { return tr(DESCRIPTION); }
   699   virtual bool Start(void);
   700   virtual void Stop(void);
   701   virtual cMenuSetupPage *SetupMenu(void);
   702   virtual bool SetupParse(const char *Name, const char *Value);
   703   };
   704 
   705 cPluginPremiereEpg::cPluginPremiereEpg(void)
   706 {
   707   memset(epg,0,sizeof(epg));
   708 }
   709 
   710 bool cPluginPremiereEpg::Start(void)
   711 {
   712   RegisterI18n(Phrases);
   713   for(int i=0; i<MAXDVBDEVICES; i++) {
   714     cDevice *dev=cDevice::GetDevice(i);
   715     if(dev) {
   716       epg[i].device=dev;
   717       dev->AttachFilter(epg[i].filter=new cFilterPremiereEpg);
   718       isyslog("Attached premiere EPG filter to device %d",i);
   719       }
   720     }
   721   return true;
   722 }
   723 
   724 void cPluginPremiereEpg::Stop(void)
   725 {
   726   for(int i=0; i<MAXDVBDEVICES; i++) {
   727     cDevice *dev=epg[i].device;
   728     if(dev) dev->Detach(epg[i].filter);
   729     delete epg[i].filter;
   730     epg[i].device=0;
   731     epg[i].filter=0;
   732     }
   733 }
   734 
   735 cMenuSetupPage *cPluginPremiereEpg::SetupMenu(void)
   736 {
   737   return new cMenuSetupPremiereEpg;
   738 }
   739 
   740 bool cPluginPremiereEpg::SetupParse(const char *Name, const char *Value)
   741 {
   742   if      (!strcasecmp(Name, "OptionPattern")) SetupPE.OptPat     = atoi(Value);
   743   else if (!strcasecmp(Name, "OrderInfo"))     SetupPE.OrderInfo  = atoi(Value);
   744   else if (!strcasecmp(Name, "RatingInfo"))    SetupPE.RatingInfo = atoi(Value);
   745   else return false;
   746   return true;
   747 }
   748 
   749 VDRPLUGINCREATOR(cPluginPremiereEpg); // Don't touch this!