premiereepg.c
author nathan
Sat, 29 Dec 2007 11:19:35 +0100
branchtrunk
changeset 8 b5bd55cfd28f
parent 6 b0218bef406f
child 10 967afc97e51d
permissions -rw-r--r--
release 0.0.5
     1 /*
     2  * PremiereEpg plugin to VDR (C++)
     3  *
     4  * (C) 2005 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 <libsi/section.h>
    33 #include <libsi/descriptor.h>
    34 
    35 //#define DEBUG
    36 //#define DEBUG2
    37 
    38 #ifdef DEBUG
    39 #define d(x) { (x); }
    40 #else
    41 #define d(x) ; 
    42 #endif
    43 #ifdef DEBUG2
    44 #define d2(x) { (x); }
    45 #else
    46 #define d2(x) ; 
    47 #endif
    48 
    49 #define PMT_SCAN_TIMEOUT  10  // seconds
    50 #define PMT_SCAN_IDLE     300 // seconds
    51 
    52 static const char *VERSION        = "0.0.5";
    53 static const char *DESCRIPTION    = "Parses extended Premiere EPG data";
    54 
    55 // --- cSetupPremiereEpg -------------------------------------------------------
    56 
    57 const char *optPats[] = {
    58   "%s",
    59   "%s (Option %d)",
    60   "%s (O%d)",
    61   "#%2$d %1$s",
    62   "[%2$d] %1$s"
    63   };
    64 #define NUM_PATS (sizeof(optPats)/sizeof(char *))
    65 
    66 class cSetupPremiereEpg {
    67 public:
    68   int OptPat;
    69   int OrderInfo;
    70   int RatingInfo;
    71 public:
    72   cSetupPremiereEpg(void);
    73   };
    74 
    75 cSetupPremiereEpg SetupPE;
    76 
    77 cSetupPremiereEpg::cSetupPremiereEpg(void)
    78 {
    79   OptPat=1;
    80   OrderInfo=1;
    81   RatingInfo=1;
    82 }
    83 
    84 // --- i18n --------------------------------------------------------------------
    85 
    86 const tI18nPhrase Phrases[] = {
    87 /*
    88 */
    89   { "PremiereEPG",
    90     "PremiereEPG",
    91     "", // TODO
    92     "", // TODO
    93     "", // TODO
    94     "", // TODO
    95     "", // TODO
    96     "", // TODO
    97     "", // TODO
    98     "", // TODO
    99     "", // TODO
   100     "", // TODO
   101     "", // TODO
   102     "", // TODO
   103     "", // TODO
   104     "", // TODO
   105   },
   106   { "Parses extended Premiere EPG data",
   107     "Liest erweiterte Premiere EPG Daten ein",
   108     "", // TODO
   109     "", // TODO
   110     "", // TODO
   111     "", // TODO
   112     "", // TODO
   113     "", // TODO
   114     "", // TODO
   115     "", // TODO
   116     "", // TODO
   117     "", // TODO
   118     "", // TODO
   119     "", // TODO
   120     "", // TODO
   121     "", // TODO
   122   },
   123   { "Tag option events",
   124     "Options Events markieren",
   125     "", // TODO
   126     "", // TODO
   127     "", // TODO
   128     "", // TODO
   129     "", // TODO
   130     "", // TODO
   131     "", // TODO
   132     "", // TODO
   133     "", // TODO
   134     "", // TODO
   135     "", // TODO
   136     "", // TODO
   137     "", // TODO
   138     "", // TODO
   139   },
   140   { "Show order information",
   141     "Bestellhinweise anzeigen",
   142     "", // TODO
   143     "", // TODO
   144     "", // TODO
   145     "", // TODO
   146     "", // TODO
   147     "", // TODO
   148     "", // TODO
   149     "", // TODO
   150     "", // TODO
   151     "", // TODO
   152     "", // TODO
   153     "", // TODO
   154     "", // TODO
   155     "", // TODO
   156   },
   157   { "Show rating information",
   158     "Altersfreigaben anzeigen",
   159     "", // TODO
   160     "", // TODO
   161     "", // TODO
   162     "", // TODO
   163     "", // TODO
   164     "", // TODO
   165     "", // TODO
   166     "", // TODO
   167     "", // TODO
   168     "", // TODO
   169     "", // TODO
   170     "", // TODO
   171     "", // TODO
   172     "", // TODO
   173   },
   174 
   175   { "Ordernumber",
   176     "Bestellnummer",
   177     "", // TODO
   178     "", // TODO
   179     "", // TODO
   180     "", // TODO
   181     "", // TODO
   182     "", // TODO
   183     "", // TODO
   184     "", // TODO
   185     "", // TODO
   186     "", // TODO
   187     "", // TODO
   188     "", // TODO
   189     "", // TODO
   190     "", // TODO
   191   },
   192   { "Price",
   193     "Preis",
   194     "", // TODO
   195     "", // TODO
   196     "", // TODO
   197     "", // TODO
   198     "", // TODO
   199     "", // TODO
   200     "", // TODO
   201     "", // TODO
   202     "", // TODO
   203     "", // TODO
   204     "", // TODO
   205     "", // TODO
   206     "", // TODO
   207     "", // TODO
   208   },
   209   { "Ordering",
   210     "Bestellen",
   211     "", // TODO
   212     "", // TODO
   213     "", // TODO
   214     "", // TODO
   215     "", // TODO
   216     "", // TODO
   217     "", // TODO
   218     "", // TODO
   219     "", // TODO
   220     "", // TODO
   221     "", // TODO
   222     "", // TODO
   223     "", // TODO
   224     "", // TODO
   225   },
   226   { "SMS",
   227     "SMS",
   228     "", // TODO
   229     "", // TODO
   230     "", // TODO
   231     "", // TODO
   232     "", // TODO
   233     "", // TODO
   234     "", // TODO
   235     "", // TODO
   236     "", // TODO
   237     "", // TODO
   238     "", // TODO
   239     "", // TODO
   240     "", // TODO
   241     "", // TODO
   242   },
   243   { "WWW",
   244     "WWW",
   245     "", // TODO
   246     "", // TODO
   247     "", // TODO
   248     "", // TODO
   249     "", // TODO
   250     "", // TODO
   251     "", // TODO
   252     "", // TODO
   253     "", // TODO
   254     "", // TODO
   255     "", // TODO
   256     "", // TODO
   257     "", // TODO
   258     "", // TODO
   259   },
   260   { "Rating",
   261     "Altersfreigabe",
   262     "", // TODO
   263     "", // TODO
   264     "", // TODO
   265     "", // TODO
   266     "", // TODO
   267     "", // TODO
   268     "", // TODO
   269     "", // TODO
   270     "", // TODO
   271     "", // TODO
   272     "", // TODO
   273     "", // TODO
   274     "", // TODO
   275     "", // TODO
   276   },
   277   { "years",
   278     "Jahre",
   279     "", // TODO
   280     "", // TODO
   281     "", // TODO
   282     "", // TODO
   283     "", // TODO
   284     "", // TODO
   285     "", // TODO
   286     "", // TODO
   287     "", // TODO
   288     "", // TODO
   289     "", // TODO
   290     "", // TODO
   291     "", // TODO
   292     "", // TODO
   293   },
   294 
   295   { NULL }
   296   };
   297 
   298 // --- cMenuSetupPremiereEpg ------------------------------------------------------------
   299 
   300 class cMenuSetupPremiereEpg : public cMenuSetupPage {
   301 private:
   302   cSetupPremiereEpg data;
   303   const char *optDisp[NUM_PATS];
   304   char buff[NUM_PATS][32];
   305 protected:
   306   virtual void Store(void);
   307 public:
   308   cMenuSetupPremiereEpg(void);
   309   };
   310 
   311 cMenuSetupPremiereEpg::cMenuSetupPremiereEpg(void)
   312 {
   313   data=SetupPE;
   314   SetSection(tr("PremiereEPG"));
   315   optDisp[0]=tr("off");
   316   for(unsigned int i=1; i<NUM_PATS; i++) {
   317     snprintf(buff[i],sizeof(buff[i]),optPats[i],"Event",1);
   318     optDisp[i]=buff[i];
   319     }
   320   Add(new cMenuEditStraItem(tr("Tag option events"),&data.OptPat,NUM_PATS,optDisp));
   321   Add(new cMenuEditBoolItem(tr("Show order information"),&data.OrderInfo));
   322   Add(new cMenuEditBoolItem(tr("Show rating information"),&data.RatingInfo));
   323 }
   324 
   325 void cMenuSetupPremiereEpg::Store(void)
   326 {
   327   SetupPE=data;
   328   SetupStore("OptionPattern",SetupPE.OptPat);
   329   SetupStore("OrderInfo",SetupPE.OrderInfo);
   330   SetupStore("RatingInfo",SetupPE.RatingInfo);
   331 }
   332 
   333 // --- CIT ---------------------------------------------------------------------
   334 
   335 namespace SI {
   336 
   337 #define CIT_LEN 17
   338 
   339 struct cit {
   340    u_char table_id                               :8;
   341 #if BYTE_ORDER == BIG_ENDIAN
   342    u_char section_syntax_indicator               :1;
   343    u_char                                        :3;
   344    u_char section_length_hi                      :4;
   345 #else
   346    u_char section_length_hi                      :4;
   347    u_char                                        :3;
   348    u_char section_syntax_indicator               :1;
   349 #endif
   350    u_char section_length_lo                      :8;
   351    u_char service_id_hi                          :8;
   352    u_char service_id_lo                          :8;
   353 #if BYTE_ORDER == BIG_ENDIAN
   354    u_char                                        :2;
   355    u_char version_number                         :5;
   356    u_char current_next_indicator                 :1;
   357 #else
   358    u_char current_next_indicator                 :1;
   359    u_char version_number                         :5;
   360    u_char                                        :2;
   361 #endif
   362    u_char section_number                         :8;
   363    u_char last_section_number                    :8;
   364    u_char content_id_hi_hi                       :8;
   365    u_char content_id_hi_lo                       :8;
   366    u_char content_id_lo_hi                       :8;
   367    u_char content_id_lo_lo                       :8;
   368    u_char duration_h                             :8;
   369    u_char duration_m                             :8;
   370    u_char duration_s                             :8;
   371 #if BYTE_ORDER == BIG_ENDIAN
   372    u_char                                        :4;
   373    u_char descriptors_loop_length_hi             :4;
   374 #else
   375    u_char descriptors_loop_length_hi             :4;
   376    u_char                                        :4;
   377 #endif
   378    u_char descriptors_loop_length_lo             :8;
   379 };
   380 
   381 class CIT : public NumberedSection {
   382 public:
   383    CIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
   384    CIT() {}
   385    int getContentId(void) const;
   386    time_t getDuration(void) const;
   387    DescriptorLoop eventDescriptors;
   388 protected:
   389    virtual void Parse(void);
   390 private:
   391    const cit *s;
   392 };
   393 
   394 int CIT::getContentId(void) const {
   395    return (HILO(s->content_id_hi)<<16) | (HILO(s->content_id_lo));
   396 }
   397 
   398 time_t CIT::getDuration(void) const {
   399    return DVBTime::getDuration(s->duration_h,s->duration_m,s->duration_s);
   400 }
   401 
   402 void CIT::Parse(void) {
   403 #if VDRVERSNUM >= 10343
   404    int offset=0;
   405 #else
   406    unsigned int offset=0;
   407 #endif
   408    data.setPointerAndOffset<const cit>(s, offset);
   409    eventDescriptors.setData(data+offset,HILO(s->descriptors_loop_length));
   410 }
   411 
   412 } // end of namespace
   413 
   414 // --- cDescrF2 ----------------------------------------------------------------
   415 
   416 class cDescrF2 {
   417 private:
   418   SI::Descriptor *d;
   419   SI::CharArray data;
   420   int idx, loop, nloop, index;
   421 public:
   422   cDescrF2(SI::Descriptor *D);
   423   int TransportStreamId(void) { return data.TwoBytes(2); }
   424   int OrgNetworkId(void)      { return data.TwoBytes(4); }
   425   int ServiceId(void)         { return data.TwoBytes(6); }
   426   void Start(void);
   427   bool Next(void);
   428   time_t StartTime(void);
   429   int Index(void) { return index; }
   430   };
   431 
   432 cDescrF2::cDescrF2(SI::Descriptor *D)
   433 {
   434   d=D;
   435   data=d->getData();
   436   Start();
   437 }
   438 
   439 void cDescrF2::Start(void)
   440 {
   441   idx=8; loop=0; nloop=-3; index=-1;
   442 }
   443 
   444 bool cDescrF2::Next(void)
   445 {
   446   loop+=3;
   447   if(loop>=nloop) {
   448     idx+=nloop+3;
   449     if(idx>=d->getLength()) return false;
   450     loop=0; nloop=data[idx+2];
   451     }
   452  index++;
   453  return true;
   454 }
   455 
   456 time_t cDescrF2::StartTime(void)
   457 {
   458   int off=idx+3+loop;
   459   return SI::DVBTime::getTime(data[idx+0],data[idx+1],data[off+0],data[off+1],data[off+2]);
   460 }
   461 
   462 // --- cFilterPremiereEpg ------------------------------------------------------
   463 
   464 class cFilterPremiereEpg : public cFilter {
   465 private:
   466   int pmtpid, pmtidx, pmtnext;
   467   //
   468   void NextPmt(void);
   469 protected:
   470   virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
   471 public:
   472   cFilterPremiereEpg(void);
   473   virtual void SetStatus(bool On);
   474   void Trigger(void);
   475   };
   476 
   477 cFilterPremiereEpg::cFilterPremiereEpg(void)
   478 {
   479   Trigger();
   480   Set(0x00,0x00);
   481 }
   482 
   483 void cFilterPremiereEpg::Trigger(void)
   484 {
   485   d(printf("trigger\n"))
   486   pmtpid=0; pmtidx=0; pmtnext=0;
   487 }
   488 
   489 void cFilterPremiereEpg::SetStatus(bool On)
   490 {
   491   d(printf("setstatus %d\n",On))
   492   cFilter::SetStatus(On);
   493   Trigger();
   494 }
   495 
   496 void cFilterPremiereEpg::NextPmt(void)
   497 {
   498   Del(pmtpid,0x02);
   499   pmtpid=0;
   500   pmtidx++;
   501   d(printf("PMT next\n"))
   502 }
   503 
   504 void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
   505 {
   506   if(Pid==0 && Tid==SI::TableIdPAT) {
   507     int now=time(0);
   508     if(!pmtnext || now>pmtnext) {
   509       if(pmtpid) NextPmt();
   510       if(!pmtpid) {
   511         SI::PAT pat(Data,false);
   512         if(pat.CheckCRCAndParse()) {
   513           SI::PAT::Association assoc;
   514           int idx=0;
   515           for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
   516             if(!assoc.isNITPid()) {
   517               if(idx++==pmtidx) {
   518                 pmtpid=assoc.getPid();
   519                 Add(pmtpid,0x02);
   520                 pmtnext=now+PMT_SCAN_TIMEOUT;
   521                 d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx))
   522                 break;
   523                 }
   524               }
   525             }
   526           if(!pmtpid) {
   527             pmtidx=0;
   528             pmtnext=now+PMT_SCAN_IDLE;
   529             d(printf("PMT scan idle\n"))
   530             }
   531           }
   532         }
   533       }
   534     }
   535   else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) {
   536     SI::PMT pmt(Data,false);
   537     if(pmt.CheckCRCAndParse()) {
   538       SI::PMT::Stream stream;
   539       for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
   540         if(stream.getStreamType()==0x05) {
   541           SI::CharArray data=stream.getData();
   542           if((data[1]&0xE0)==0xE0 && (data[3]&0xF0)==0xF0) {
   543             bool prvData=false, usrData=false;
   544             SI::Descriptor *d;
   545             for(SI::Loop::Iterator it; (d=stream.streamDescriptors.getNext(it)); ) {
   546               switch(d->getDescriptorTag()) {
   547                 case SI::PrivateDataSpecifierDescriptorTag:
   548                   d(printf("prv: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   549                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x000000be)
   550                     prvData=true;
   551                   break;
   552                 case 0x90:
   553                   d(printf("usr: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   554                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x0000ffff)
   555                     usrData=true;
   556                   break;
   557                 default:
   558                   break;
   559                 }
   560               delete d;
   561               }
   562             if(prvData && usrData) {
   563               int pid=stream.getPid();
   564               d(printf("found citpid 0x%04x",pid))
   565               if(!Matches(pid,0xA0)) {
   566                 Add(pid,0xA0);
   567                 d(printf(" (added)"))
   568                 }
   569               d(printf("\n"))
   570               }
   571             }
   572           }
   573         }
   574       NextPmt(); pmtnext=0;
   575       }
   576     }
   577   else if(Tid==0xA0 && Source()) {
   578     SI::CIT cit(Data,false);
   579     if(cit.CheckCRCAndParse()) {
   580       cSchedulesLock SchedulesLock(true,10);
   581       cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock);
   582       if(Schedules) {
   583         int nCount=0;
   584         time_t firstTime=0;
   585         SI::Descriptor *d;
   586         int LanguagePreferenceShort=-1;
   587         int LanguagePreferenceExt=-1;
   588         bool UseExtendedEventDescriptor=false;
   589         SI::ExtendedEventDescriptors *ExtendedEventDescriptors=0;
   590         SI::ShortEventDescriptor *ShortEventDescriptor=0;
   591         char *order=0, *rating=0;
   592         for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
   593           switch(d->getDescriptorTag()) {
   594             case 0xF0: // order information
   595               if(SetupPE.OrderInfo) {
   596                 static const char *text[] = {
   597                   "Ordernumber",
   598                   "Price",
   599                   "Ordering",
   600                   "SMS",
   601                   "WWW"
   602                   };
   603                 char buff[512];
   604                 int p=0;
   605                 const unsigned char *data=d->getData().getData()+2;
   606                 for(int i=0; i<5; i++) {
   607                   int l=data[0]; 
   608                   if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %.*s",tr(text[i]),l,&data[1]);
   609                   data+=l+1;
   610                   }
   611                 if(p>0) order=strdup(buff);
   612                 }
   613               break;
   614             case 0xF1: // parental rating
   615               if(SetupPE.RatingInfo) {
   616                 char buff[512];
   617                 int p=0;
   618                 const unsigned char *data=d->getData().getData()+2;
   619                 p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %d %s",tr("Rating"),data[0]+3,tr("years"));
   620                 data+=7;
   621                 int l=data[0]; 
   622                 if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p," (%.*s)",l,&data[1]);
   623                 if(p>0) rating=strdup(buff);
   624                 }
   625               break;
   626             case 0xF2: // transmisions
   627               if(nCount>=0) {
   628                 nCount++;
   629                 cDescrF2 f2(d);
   630                 if(f2.Next()) {
   631                   if(nCount==1) firstTime=f2.StartTime();
   632                   else {
   633                     time_t time=f2.StartTime();
   634                     if(firstTime<time-5*50 || firstTime>time+5*60)
   635                       nCount=-1;
   636                     }
   637                   }
   638                 }
   639               break;
   640             case SI::ExtendedEventDescriptorTag:
   641               {
   642               SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d;
   643 #if VDRVERSNUM < 10332
   644               if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(eed->languageCode), LanguagePreferenceExt) || !ExtendedEventDescriptors) {
   645 #else
   646               if(I18nIsPreferredLanguage(Setup.EPGLanguages,eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
   647 #endif
   648                  delete ExtendedEventDescriptors;
   649                  ExtendedEventDescriptors=new SI::ExtendedEventDescriptors;
   650                  UseExtendedEventDescriptor=true;
   651                  }
   652               if(UseExtendedEventDescriptor) {
   653                  ExtendedEventDescriptors->Add(eed);
   654                  d=NULL; // so that it is not deleted
   655                  }
   656               if(eed->getDescriptorNumber()==eed->getLastDescriptorNumber())
   657                  UseExtendedEventDescriptor=false;
   658               }
   659               break;
   660             case SI::ShortEventDescriptorTag:
   661               {
   662               SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d;
   663 #if VDRVERSNUM < 10332
   664               if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(sed->languageCode), LanguagePreferenceShort) || !ShortEventDescriptor) {
   665 #else
   666               if(I18nIsPreferredLanguage(Setup.EPGLanguages,sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
   667 #endif
   668                  delete ShortEventDescriptor;
   669                  ShortEventDescriptor=sed;
   670                  d=NULL; // so that it is not deleted
   671                  }
   672               }
   673               break;
   674             default:
   675               break;
   676             }
   677           delete d;
   678           }
   679 
   680         bool Modified=false;
   681         int optCount=0;
   682         for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
   683           if(d->getDescriptorTag()==0xF2) {
   684             optCount++;
   685 
   686             cDescrF2 f2(d);
   687             tChannelID channelID(Source(),f2.OrgNetworkId(),f2.TransportStreamId(),f2.ServiceId());
   688             cChannel *channel=Channels.GetByChannelID(channelID,true);
   689             if(!channel) continue;
   690 
   691             cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
   692             if(!pSchedule) {
   693                pSchedule=new cSchedule(channelID);
   694                Schedules->Add(pSchedule);
   695                }
   696 
   697             for(f2.Start(); f2.Next();) {
   698               u_int16_t EventId=(cit.getContentId()<<4) | f2.Index();
   699               time_t StartTime=f2.StartTime();
   700 
   701               bool isOpt=false;
   702               if(f2.Index()==0 && nCount>1) isOpt=true;
   703 
   704               d2(printf("ch=%s id=%04x/%08x %d opt=%d/%d st=%s ",*channelID.ToString(),EventId,cit.getContentId(),f2.Index(),isOpt,optCount,stripspace(ctime(&StartTime))))
   705               if(StartTime+cit.getDuration()+Setup.EPGLinger*60<time(0)) {
   706                 d2(printf("(old)\n"))
   707                 continue;
   708                 }
   709 
   710               bool newEvent=false;
   711               cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,StartTime);
   712               if(!pEvent) {
   713                  d2(printf("(new)\n"))
   714 #if VDRVERSNUM >= 10325
   715                  pEvent=new cEvent(EventId);
   716 #else
   717                  pEvent=new cEvent(channelID,EventId);
   718 #endif
   719                  if(!pEvent) continue;
   720                  newEvent=true;
   721                  }
   722               else {
   723                  d2(printf("(upd)\n"))
   724                  pEvent->SetSeen();
   725                  if(pEvent->TableID()==0x00) continue;
   726                  if(Tid==pEvent->TableID() && pEvent->Version()==cit.getVersionNumber()) continue;
   727                  }
   728               pEvent->SetEventID(EventId);
   729               pEvent->SetTableID(Tid);
   730               pEvent->SetVersion(cit.getVersionNumber());
   731               pEvent->SetStartTime(StartTime);
   732               pEvent->SetDuration(cit.getDuration());
   733 
   734               if(ShortEventDescriptor) {
   735                 char buffer[256];
   736                 ShortEventDescriptor->name.getText(buffer,sizeof(buffer));
   737                 if(isOpt) {
   738                   char buffer2[sizeof(buffer)+32];
   739                   snprintf(buffer2,sizeof(buffer2),optPats[SetupPE.OptPat],buffer,optCount);
   740                   pEvent->SetTitle(buffer2);
   741                   }
   742                 else
   743                   pEvent->SetTitle(buffer);
   744                 pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer)));
   745                 }
   746               if(ExtendedEventDescriptors) {
   747                 char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ")+1];
   748                 pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer,sizeof(buffer),": "));
   749                 }
   750               if(order || rating) {
   751                 int len=(pEvent->Description() ? strlen(pEvent->Description()) : 0) +
   752                         (order                 ? strlen(order) : 0) +
   753                         (rating                ? strlen(rating) : 0);
   754                 char buffer[len+32];
   755                 buffer[0]=0;
   756                 if(pEvent->Description()) strcat(buffer,pEvent->Description());
   757                 if(rating)                strcat(buffer,rating);
   758                 if(order)                 strcat(buffer,order);
   759                 pEvent->SetDescription(buffer);
   760                 }
   761 
   762               if(newEvent) pSchedule->AddEvent(pEvent);
   763 #if VDRVERSNUM >= 10318
   764               pEvent->SetComponents(NULL);
   765 #endif
   766               pEvent->FixEpgBugs();
   767               Modified=true;
   768               }
   769             if(Modified) {
   770               pSchedule->Sort();
   771               Schedules->SetModified(pSchedule);
   772               }
   773             }
   774           delete d;
   775           }
   776         delete ExtendedEventDescriptors;
   777         delete ShortEventDescriptor;
   778         free(order);
   779         free(rating);
   780         }
   781       }
   782     }
   783 }
   784 
   785 // --- cPluginPremiereEpg ------------------------------------------------------
   786 
   787 class cPluginPremiereEpg : public cPlugin {
   788 private:
   789   struct {
   790     cFilterPremiereEpg *filter;
   791     cDevice *device;
   792     } epg[MAXDVBDEVICES];
   793 public:
   794   cPluginPremiereEpg(void);
   795   virtual const char *Version(void) { return VERSION; }
   796   virtual const char *Description(void) { return tr(DESCRIPTION); }
   797   virtual bool Start(void);
   798   virtual void Stop(void);
   799   virtual cMenuSetupPage *SetupMenu(void);
   800   virtual bool SetupParse(const char *Name, const char *Value);
   801   };
   802 
   803 cPluginPremiereEpg::cPluginPremiereEpg(void)
   804 {
   805   memset(epg,0,sizeof(epg));
   806 }
   807 
   808 bool cPluginPremiereEpg::Start(void)
   809 {
   810   RegisterI18n(Phrases);
   811   for(int i=0; i<MAXDVBDEVICES; i++) {
   812     cDevice *dev=cDevice::GetDevice(i);
   813     if(dev) {
   814       epg[i].device=dev;
   815       dev->AttachFilter(epg[i].filter=new cFilterPremiereEpg);
   816       isyslog("Attached premiere EPG filter to device %d",i);
   817       }
   818     }
   819   return true;
   820 }
   821 
   822 void cPluginPremiereEpg::Stop(void)
   823 {
   824   for(int i=0; i<MAXDVBDEVICES; i++) {
   825     cDevice *dev=epg[i].device;
   826     if(dev) dev->Detach(epg[i].filter);
   827     delete epg[i].filter;
   828     epg[i].device=0;
   829     epg[i].filter=0;
   830     }
   831 }
   832 
   833 cMenuSetupPage *cPluginPremiereEpg::SetupMenu(void)
   834 {
   835   return new cMenuSetupPremiereEpg;
   836 }
   837 
   838 bool cPluginPremiereEpg::SetupParse(const char *Name, const char *Value)
   839 {
   840   if      (!strcasecmp(Name, "OptionPattern")) SetupPE.OptPat     = atoi(Value);
   841   else if (!strcasecmp(Name, "OrderInfo"))     SetupPE.OrderInfo  = atoi(Value);
   842   else if (!strcasecmp(Name, "RatingInfo"))    SetupPE.RatingInfo = atoi(Value);
   843   else return false;
   844   return true;
   845 }
   846 
   847 VDRPLUGINCREATOR(cPluginPremiereEpg); // Don't touch this!