premiereepg.c
author nathan
Mon, 28 Jan 2008 17:21:58 +0100
branchtrunk
changeset 21 bc68e23ad6d5
parent 20 bc64e11172f5
child 23 3c10fdd8ccce
permissions -rw-r--r--
rework version generation
     1 /*
     2  * PremiereEpg plugin to VDR (C++)
     3  *
     4  * (C) 2005-2007 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 #include "version.h"
    36 
    37 #if APIVERSNUM < 10401
    38 #error You need at least VDR API version 1.4.1 for this plugin
    39 #endif
    40 #if APIVERSNUM < 10507
    41 #define trNOOP(s) (s)
    42 #endif
    43 
    44 //#define DEBUG
    45 //#define DEBUG2
    46 
    47 #ifdef DEBUG
    48 #define d(x) { (x); }
    49 #else
    50 #define d(x) ; 
    51 #endif
    52 #ifdef DEBUG2
    53 #define d2(x) { (x); }
    54 #else
    55 #define d2(x) ; 
    56 #endif
    57 
    58 #define PMT_SCAN_TIMEOUT  10  // seconds
    59 #define PMT_SCAN_IDLE     300 // seconds
    60 
    61 // --- cSetupPremiereEpg -------------------------------------------------------
    62 
    63 const char *optPats[] = {
    64   "%s",
    65   "%s (Option %d)",
    66   "%s (O%d)",
    67   "#%2$d %1$s",
    68   "[%2$d] %1$s"
    69   };
    70 #define NUM_PATS (sizeof(optPats)/sizeof(char *))
    71 
    72 class cSetupPremiereEpg {
    73 public:
    74   int OptPat;
    75   int OrderInfo;
    76   int RatingInfo;
    77   int FixEpg;
    78 public:
    79   cSetupPremiereEpg(void);
    80   };
    81 
    82 cSetupPremiereEpg SetupPE;
    83 
    84 cSetupPremiereEpg::cSetupPremiereEpg(void)
    85 {
    86   OptPat=1;
    87   OrderInfo=1;
    88   RatingInfo=1;
    89   FixEpg=0;
    90 }
    91 
    92 // --- i18n --------------------------------------------------------------------
    93 
    94 #if APIVERSNUM < 10507
    95 const tI18nPhrase Phrases[] = {
    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   { "off",
   131     "aus",
   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   { "Tag option events",
   148     "Options Events markieren",
   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 order information",
   165     "Bestellhinweise 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   { "Show rating information",
   182     "Altersfreigaben anzeigen",
   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     "", // TODO
   197   },
   198 
   199   { "Ordernumber",
   200     "Bestellnummer",
   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   { "Price",
   217     "Preis",
   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   { "Ordering",
   234     "Bestellen",
   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   { "SMS",
   251     "SMS",
   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   { "WWW",
   268     "WWW",
   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   { "Rating",
   285     "Altersfreigabe",
   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   { "years",
   302     "Jahre",
   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   { "Fix EPG data",
   319     "EPG Daten korrigieren",
   320     "", // TODO
   321     "", // TODO
   322     "", // TODO
   323     "", // TODO
   324     "", // TODO
   325     "", // TODO
   326     "", // TODO
   327     "", // TODO
   328     "", // TODO
   329     "", // TODO
   330     "", // TODO
   331     "", // TODO
   332     "", // TODO
   333     "", // TODO
   334   },
   335 
   336   { NULL }
   337   };
   338 #endif
   339 
   340 // --- cMenuSetupPremiereEpg ------------------------------------------------------------
   341 
   342 class cMenuSetupPremiereEpg : public cMenuSetupPage {
   343 private:
   344   cSetupPremiereEpg data;
   345   const char *optDisp[NUM_PATS];
   346   char buff[NUM_PATS][32];
   347 protected:
   348   virtual void Store(void);
   349 public:
   350   cMenuSetupPremiereEpg(void);
   351   };
   352 
   353 cMenuSetupPremiereEpg::cMenuSetupPremiereEpg(void)
   354 {
   355   data=SetupPE;
   356   SetSection(tr("PremiereEPG"));
   357   optDisp[0]=tr("off");
   358   for(unsigned int i=1; i<NUM_PATS; i++) {
   359     snprintf(buff[i],sizeof(buff[i]),optPats[i],"Event",1);
   360     optDisp[i]=buff[i];
   361     }
   362   Add(new cMenuEditStraItem(tr("Tag option events"),&data.OptPat,NUM_PATS,optDisp));
   363   Add(new cMenuEditBoolItem(tr("Show order information"),&data.OrderInfo));
   364   Add(new cMenuEditBoolItem(tr("Show rating information"),&data.RatingInfo));
   365   Add(new cMenuEditBoolItem(tr("Fix EPG data"),&data.FixEpg));
   366 }
   367 
   368 void cMenuSetupPremiereEpg::Store(void)
   369 {
   370   SetupPE=data;
   371   SetupStore("OptionPattern",SetupPE.OptPat);
   372   SetupStore("OrderInfo",SetupPE.OrderInfo);
   373   SetupStore("RatingInfo",SetupPE.RatingInfo);
   374   SetupStore("FixEpg",SetupPE.FixEpg);
   375 }
   376 
   377 // --- CRC16 -------------------------------------------------------------------
   378 
   379 #define POLY 0xA001 // CRC16
   380 
   381 unsigned int crc16(unsigned int crc, unsigned char const *p, int len)
   382 {
   383   while(len--) {
   384     crc^=*p++;
   385     for(int i=0; i<8; i++)
   386       crc=(crc&1) ? (crc>>1)^POLY : (crc>>1);
   387     }
   388   return crc&0xFFFF;
   389 }
   390 
   391 // --- cFilterPremiereEpg ------------------------------------------------------
   392 
   393 #define STARTTIME_BIAS (20*60)
   394 
   395 class cFilterPremiereEpg : public cFilter {
   396 private:
   397   int pmtpid, pmtsid, pmtidx, pmtnext;
   398   //
   399   void NextPmt(void);
   400 protected:
   401   virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
   402 public:
   403   cFilterPremiereEpg(void);
   404   virtual void SetStatus(bool On);
   405   void Trigger(void);
   406   };
   407 
   408 cFilterPremiereEpg::cFilterPremiereEpg(void)
   409 {
   410   Trigger();
   411   Set(0x00,0x00);
   412 }
   413 
   414 void cFilterPremiereEpg::Trigger(void)
   415 {
   416   d(printf("trigger\n"))
   417   pmtpid=0; pmtidx=0; pmtnext=0;
   418 }
   419 
   420 void cFilterPremiereEpg::SetStatus(bool On)
   421 {
   422   d(printf("setstatus %d\n",On))
   423   cFilter::SetStatus(On);
   424   Trigger();
   425 }
   426 
   427 void cFilterPremiereEpg::NextPmt(void)
   428 {
   429   Del(pmtpid,0x02);
   430   pmtpid=0;
   431   pmtidx++;
   432   d(printf("PMT next\n"))
   433 }
   434 
   435 void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
   436 {
   437   int now=time(0);
   438   if(Pid==0 && Tid==SI::TableIdPAT) {
   439     if(!pmtnext || now>pmtnext) {
   440       if(pmtpid) NextPmt();
   441       if(!pmtpid) {
   442         SI::PAT pat(Data,false);
   443         if(pat.CheckCRCAndParse()) {
   444           SI::PAT::Association assoc;
   445           int idx=0;
   446           for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
   447             if(!assoc.isNITPid()) {
   448               if(idx++==pmtidx) {
   449                 pmtpid=assoc.getPid();
   450                 pmtsid=assoc.getServiceId();
   451                 Add(pmtpid,0x02);
   452                 pmtnext=now+PMT_SCAN_TIMEOUT;
   453                 d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx))
   454                 break;
   455                 }
   456               }
   457             }
   458           if(!pmtpid) {
   459             pmtidx=0;
   460             pmtnext=now+PMT_SCAN_IDLE;
   461             d(printf("PMT scan idle\n"))
   462             }
   463           }
   464         }
   465       }
   466     }
   467   else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) {
   468     SI::PMT pmt(Data,false);
   469     if(pmt.CheckCRCAndParse() && pmt.getServiceId()==pmtsid) {
   470       SI::PMT::Stream stream;
   471       for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
   472         if(stream.getStreamType()==0x05) {
   473           SI::CharArray data=stream.getData();
   474           if((data[1]&0xE0)==0xE0 && (data[3]&0xF0)==0xF0) {
   475             bool prvData=false, usrData=false;
   476             SI::Descriptor *d;
   477             for(SI::Loop::Iterator it; (d=stream.streamDescriptors.getNext(it)); ) {
   478               switch(d->getDescriptorTag()) {
   479                 case SI::PrivateDataSpecifierDescriptorTag:
   480                   d(printf("prv: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   481                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x000000be)
   482                     prvData=true;
   483                   break;
   484                 case 0x90:
   485                   d(printf("usr: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   486                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x0000ffff)
   487                     usrData=true;
   488                   break;
   489                 default:
   490                   break;
   491                 }
   492               delete d;
   493               }
   494             if(prvData && usrData) {
   495               int pid=stream.getPid();
   496               d(printf("found citpid 0x%04x",pid))
   497               if(!Matches(pid,0xA0)) {
   498                 Add(pid,0xA0);
   499                 d(printf(" (added)"))
   500                 }
   501               d(printf("\n"))
   502               }
   503             }
   504           }
   505         }
   506       NextPmt(); pmtnext=0;
   507       }
   508     }
   509   else if(Tid==0xA0 && Source()) {
   510     SI::PremiereCIT cit(Data,false);
   511     if(cit.CheckCRCAndParse()) {
   512       cSchedulesLock SchedulesLock(true,10);
   513       cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock);
   514       if(Schedules) {
   515         int nCount=0;
   516         SI::ExtendedEventDescriptors *ExtendedEventDescriptors=0;
   517         SI::ShortEventDescriptor *ShortEventDescriptor=0;
   518         char *order=0, *rating=0;
   519         {
   520         time_t firstTime=0;
   521         SI::Descriptor *d;
   522         bool UseExtendedEventDescriptor=false;
   523         int LanguagePreferenceShort=-1;
   524         int LanguagePreferenceExt=-1;
   525         for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
   526           switch(d->getDescriptorTag()) {
   527             case 0xF0: // order information
   528               if(SetupPE.OrderInfo) {
   529                 static const char *text[] = {
   530                   trNOOP("Ordernumber"),
   531                   trNOOP("Price"),
   532                   trNOOP("Ordering"),
   533                   trNOOP("SMS"),
   534                   trNOOP("WWW")
   535                   };
   536                 char buff[512];
   537                 int p=0;
   538                 const unsigned char *data=d->getData().getData()+2;
   539                 for(int i=0; i<5; i++) {
   540                   int l=data[0]; 
   541                   if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %.*s",tr(text[i]),l,&data[1]);
   542                   data+=l+1;
   543                   }
   544                 if(p>0) order=strdup(buff);
   545                 }
   546               break;
   547             case 0xF1: // parental rating
   548               if(SetupPE.RatingInfo) {
   549                 char buff[512];
   550                 int p=0;
   551                 const unsigned char *data=d->getData().getData()+2;
   552                 p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %d %s",tr("Rating"),data[0]+3,tr("years"));
   553                 data+=7;
   554                 int l=data[0]; 
   555                 if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p," (%.*s)",l,&data[1]);
   556                 if(p>0) rating=strdup(buff);
   557                 }
   558               break;
   559             case SI::PremiereContentTransmissionDescriptorTag:
   560               if(nCount>=0) {
   561                 SI::PremiereContentTransmissionDescriptor *pct=(SI::PremiereContentTransmissionDescriptor *)d;
   562                 nCount++;
   563                 SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
   564                 SI::Loop::Iterator it;
   565                 if(pct->startDayLoop.getNext(sd,it)) {
   566                   SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
   567                   SI::Loop::Iterator it2;
   568                   if(sd.startTimeLoop.getNext(st,it2)) {
   569                     time_t StartTime=st.getStartTime(sd.getMJD());
   570                     if(nCount==1) firstTime=StartTime;
   571                     else if(firstTime<StartTime-5*50 || firstTime>StartTime+5*60)
   572                       nCount=-1;
   573                     }
   574                   }
   575                 }
   576               break;
   577             case SI::ExtendedEventDescriptorTag:
   578               {
   579               SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d;
   580               if(I18nIsPreferredLanguage(Setup.EPGLanguages,eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
   581                  delete ExtendedEventDescriptors;
   582                  ExtendedEventDescriptors=new SI::ExtendedEventDescriptors;
   583                  UseExtendedEventDescriptor=true;
   584                  }
   585               if(UseExtendedEventDescriptor) {
   586                  ExtendedEventDescriptors->Add(eed);
   587                  d=NULL; // so that it is not deleted
   588                  }
   589               if(eed->getDescriptorNumber()==eed->getLastDescriptorNumber())
   590                  UseExtendedEventDescriptor=false;
   591               }
   592               break;
   593             case SI::ShortEventDescriptorTag:
   594               {
   595               SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d;
   596               if(I18nIsPreferredLanguage(Setup.EPGLanguages,sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
   597                  delete ShortEventDescriptor;
   598                  ShortEventDescriptor=sed;
   599                  d=NULL; // so that it is not deleted
   600                  }
   601               }
   602               break;
   603             default:
   604               break;
   605             }
   606           delete d;
   607           }
   608         }
   609 
   610         {
   611         bool Modified=false;
   612         int optCount=0;
   613         unsigned int crc[3];
   614         crc[0]=cit.getContentId();
   615         SI::PremiereContentTransmissionDescriptor *pct;
   616         for(SI::Loop::Iterator it; (pct=(SI::PremiereContentTransmissionDescriptor *)cit.eventDescriptors.getNext(it,SI::PremiereContentTransmissionDescriptorTag)); ) {
   617           int nid=pct->getOriginalNetworkId();
   618           int tid=pct->getTransportStreamId();
   619           int sid=pct->getServiceId();
   620           if(SetupPE.FixEpg) {
   621             if(nid==133) {
   622 	      if     (tid==0x03 && sid==0xf0) { tid=0x02; sid=0xe0; }
   623 	      else if(tid==0x03 && sid==0xf1) { tid=0x02; sid=0xe1; }
   624 	      else if(tid==0x03 && sid==0xf5) { tid=0x03; sid=0xdc; }
   625 	      else if(tid==0x04 && sid==0xd2) { tid=0x11; sid=0xe2; }
   626 	      else if(tid==0x11 && sid==0xd3) { tid=0x11; sid=0xe3; }
   627 	      else if(tid==0x01 && sid==0xd4) { tid=0x04; sid=0xe4; }
   628               }
   629             }
   630           tChannelID channelID(Source(),nid,tid,sid);
   631           cChannel *channel=Channels.GetByChannelID(channelID,true);
   632           if(!channel) continue;
   633 
   634           cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
   635           if(!pSchedule) {
   636              pSchedule=new cSchedule(channelID);
   637              Schedules->Add(pSchedule);
   638              }
   639 
   640           optCount++;
   641           SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
   642           int index=0;
   643           for(SI::Loop::Iterator it; pct->startDayLoop.getNext(sd,it); ) {
   644             int mjd=sd.getMJD();
   645             SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
   646             for(SI::Loop::Iterator it2; sd.startTimeLoop.getNext(st,it2); ) {
   647               time_t StartTime=st.getStartTime(mjd);
   648               time_t EndTime=StartTime+cit.getDuration();
   649               int runningStatus=(StartTime<now && now<EndTime) ? SI::RunningStatusRunning : ((StartTime-30<now && now<StartTime) ? SI::RunningStatusStartsInAFewSeconds : SI::RunningStatusNotRunning);
   650               bool isOpt=false;
   651               if(index++==0 && nCount>1) isOpt=true;
   652               crc[1]=isOpt ? optCount : 0;
   653               crc[2]=StartTime / STARTTIME_BIAS;
   654               tEventID EventId=((('P'<<8)|'W')<<16) | crc16(0,(unsigned char *)crc,sizeof(crc));
   655 
   656               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))
   657               if(EndTime+Setup.EPGLinger*60<now) {
   658                 d2(printf("(old)\n"))
   659                 continue;
   660                 }
   661 
   662               bool newEvent=false;
   663               cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,-1);
   664               if(!pEvent) {
   665                 d2(printf("(new)\n"))
   666                 pEvent=new cEvent(EventId);
   667                 if(!pEvent) continue;
   668                 newEvent=true;
   669                 }
   670               else {
   671                 d2(printf("(upd)\n"))
   672                 pEvent->SetSeen();
   673                 if(pEvent->TableID()==0x00 || pEvent->Version()==cit.getVersionNumber()) {
   674                   if(pEvent->RunningStatus()!=runningStatus)
   675                     pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
   676                   continue;
   677                   }
   678                 }
   679               pEvent->SetEventID(EventId);
   680               pEvent->SetTableID(Tid);
   681               pEvent->SetVersion(cit.getVersionNumber());
   682               pEvent->SetStartTime(StartTime);
   683               pEvent->SetDuration(cit.getDuration());
   684 
   685               if(ShortEventDescriptor) {
   686                 char buffer[256];
   687                 ShortEventDescriptor->name.getText(buffer,sizeof(buffer));
   688                 if(isOpt) {
   689                   char buffer2[sizeof(buffer)+32];
   690                   snprintf(buffer2,sizeof(buffer2),optPats[SetupPE.OptPat],buffer,optCount);
   691                   pEvent->SetTitle(buffer2);
   692                   }
   693                 else
   694                   pEvent->SetTitle(buffer);
   695                 d2(printf("title: %s\n",pEvent->Title()))
   696                 pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer)));
   697                 }
   698               if(ExtendedEventDescriptors) {
   699                 char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ")+1];
   700                 pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer,sizeof(buffer),": "));
   701                 }
   702               if(order || rating) {
   703                 int len=(pEvent->Description() ? strlen(pEvent->Description()) : 0) +
   704                         (order                 ? strlen(order) : 0) +
   705                         (rating                ? strlen(rating) : 0);
   706                 char buffer[len+32];
   707                 buffer[0]=0;
   708                 if(pEvent->Description()) strcat(buffer,pEvent->Description());
   709                 if(rating)                strcat(buffer,rating);
   710                 if(order)                 strcat(buffer,order);
   711                 pEvent->SetDescription(buffer);
   712                 }
   713 
   714               if(newEvent) pSchedule->AddEvent(pEvent);
   715               pEvent->SetComponents(NULL);
   716               pEvent->FixEpgBugs();
   717               if(pEvent->RunningStatus()!=runningStatus)
   718                 pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
   719               pSchedule->DropOutdated(StartTime,EndTime,Tid,cit.getVersionNumber());
   720               Modified=true;
   721               }
   722             }
   723           if(Modified) {
   724             pSchedule->Sort();
   725             Schedules->SetModified(pSchedule);
   726             }
   727           delete pct;
   728           }
   729         }
   730         delete ExtendedEventDescriptors;
   731         delete ShortEventDescriptor;
   732         free(order);
   733         free(rating);
   734         }
   735       }
   736     }
   737 }
   738 
   739 // --- cPluginPremiereEpg ------------------------------------------------------
   740 
   741 static const char *DESCRIPTION    = trNOOP("Parses extended Premiere EPG data");
   742 
   743 class cPluginPremiereEpg : public cPlugin {
   744 private:
   745   struct {
   746     cFilterPremiereEpg *filter;
   747     cDevice *device;
   748     } epg[MAXDVBDEVICES];
   749 public:
   750   cPluginPremiereEpg(void);
   751   virtual const char *Version(void) { return PluginVersion; }
   752   virtual const char *Description(void) { return tr(DESCRIPTION); }
   753   virtual bool Start(void);
   754   virtual void Stop(void);
   755   virtual cMenuSetupPage *SetupMenu(void);
   756   virtual bool SetupParse(const char *Name, const char *Value);
   757   };
   758 
   759 cPluginPremiereEpg::cPluginPremiereEpg(void)
   760 {
   761   memset(epg,0,sizeof(epg));
   762 }
   763 
   764 bool cPluginPremiereEpg::Start(void)
   765 {
   766 #if APIVERSNUM < 10507
   767   RegisterI18n(Phrases);
   768 #endif
   769   for(int i=0; i<MAXDVBDEVICES; i++) {
   770     cDevice *dev=cDevice::GetDevice(i);
   771     if(dev) {
   772       epg[i].device=dev;
   773       dev->AttachFilter(epg[i].filter=new cFilterPremiereEpg);
   774       isyslog("Attached premiere EPG filter to device %d",i);
   775       }
   776     }
   777   return true;
   778 }
   779 
   780 void cPluginPremiereEpg::Stop(void)
   781 {
   782   for(int i=0; i<MAXDVBDEVICES; i++) {
   783     cDevice *dev=epg[i].device;
   784     if(dev) dev->Detach(epg[i].filter);
   785     delete epg[i].filter;
   786     epg[i].device=0;
   787     epg[i].filter=0;
   788     }
   789 }
   790 
   791 cMenuSetupPage *cPluginPremiereEpg::SetupMenu(void)
   792 {
   793   return new cMenuSetupPremiereEpg;
   794 }
   795 
   796 bool cPluginPremiereEpg::SetupParse(const char *Name, const char *Value)
   797 {
   798   if      (!strcasecmp(Name, "OptionPattern")) SetupPE.OptPat     = atoi(Value);
   799   else if (!strcasecmp(Name, "OrderInfo"))     SetupPE.OrderInfo  = atoi(Value);
   800   else if (!strcasecmp(Name, "RatingInfo"))    SetupPE.RatingInfo = atoi(Value);
   801   else if (!strcasecmp(Name, "FixEpg"))        SetupPE.FixEpg     = atoi(Value);
   802   else return false;
   803   return true;
   804 }
   805 
   806 VDRPLUGINCREATOR(cPluginPremiereEpg); // Don't touch this!