premiereepg.c
author nathan
Sat, 29 Dec 2007 11:19:20 +0100
branchtrunk
changeset 6 b0218bef406f
parent 4 ac6bf154890e
child 8 b5bd55cfd28f
permissions -rw-r--r--
release 0.0.4
     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.4";
    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    unsigned int offset=0;
   404    data.setPointerAndOffset<const cit>(s, offset);
   405    eventDescriptors.setData(data+offset,HILO(s->descriptors_loop_length));
   406 }
   407 
   408 } // end of namespace
   409 
   410 // --- cDescrF2 ----------------------------------------------------------------
   411 
   412 class cDescrF2 {
   413 private:
   414   SI::Descriptor *d;
   415   SI::CharArray data;
   416   int idx, loop, nloop, index;
   417 public:
   418   cDescrF2(SI::Descriptor *D);
   419   int TransportStreamId(void) { return data.TwoBytes(2); }
   420   int OrgNetworkId(void)      { return data.TwoBytes(4); }
   421   int ServiceId(void)         { return data.TwoBytes(6); }
   422   void Start(void);
   423   bool Next(void);
   424   time_t StartTime(void);
   425   int Index(void) { return index; }
   426   };
   427 
   428 cDescrF2::cDescrF2(SI::Descriptor *D)
   429 {
   430   d=D;
   431   data=d->getData();
   432   Start();
   433 }
   434 
   435 void cDescrF2::Start(void)
   436 {
   437   idx=8; loop=0; nloop=-3; index=-1;
   438 }
   439 
   440 bool cDescrF2::Next(void)
   441 {
   442   loop+=3;
   443   if(loop>=nloop) {
   444     idx+=nloop+3;
   445     if(idx>=d->getLength()) return false;
   446     loop=0; nloop=data[idx+2];
   447     }
   448  index++;
   449  return true;
   450 }
   451 
   452 time_t cDescrF2::StartTime(void)
   453 {
   454   int off=idx+3+loop;
   455   return SI::DVBTime::getTime(data[idx+0],data[idx+1],data[off+0],data[off+1],data[off+2]);
   456 }
   457 
   458 // --- cFilterPremiereEpg ------------------------------------------------------
   459 
   460 class cFilterPremiereEpg : public cFilter {
   461 private:
   462   int pmtpid, pmtidx, pmtnext;
   463   //
   464   void NextPmt(void);
   465 protected:
   466   virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
   467 public:
   468   cFilterPremiereEpg(void);
   469   virtual void SetStatus(bool On);
   470   void Trigger(void);
   471   };
   472 
   473 cFilterPremiereEpg::cFilterPremiereEpg(void)
   474 {
   475   Trigger();
   476   Set(0x00,0x00);
   477 }
   478 
   479 void cFilterPremiereEpg::Trigger(void)
   480 {
   481   d(printf("trigger\n"))
   482   pmtpid=0; pmtidx=0; pmtnext=0;
   483 }
   484 
   485 void cFilterPremiereEpg::SetStatus(bool On)
   486 {
   487   d(printf("setstatus %d\n",On))
   488   cFilter::SetStatus(On);
   489   Trigger();
   490 }
   491 
   492 void cFilterPremiereEpg::NextPmt(void)
   493 {
   494   Del(pmtpid,0x02);
   495   pmtpid=0;
   496   pmtidx++;
   497   d(printf("PMT next\n"))
   498 }
   499 
   500 void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
   501 {
   502   if(Pid==0 && Tid==SI::TableIdPAT) {
   503     int now=time(0);
   504     if(!pmtnext || now>pmtnext) {
   505       if(pmtpid) NextPmt();
   506       if(!pmtpid) {
   507         SI::PAT pat(Data,false);
   508         if(pat.CheckCRCAndParse()) {
   509           SI::PAT::Association assoc;
   510           int idx=0;
   511           for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
   512             if(!assoc.isNITPid()) {
   513               if(idx++==pmtidx) {
   514                 pmtpid=assoc.getPid();
   515                 Add(pmtpid,0x02);
   516                 pmtnext=now+PMT_SCAN_TIMEOUT;
   517                 d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx))
   518                 break;
   519                 }
   520               }
   521             }
   522           if(!pmtpid) {
   523             pmtidx=0;
   524             pmtnext=now+PMT_SCAN_IDLE;
   525             d(printf("PMT scan idle\n"))
   526             }
   527           }
   528         }
   529       }
   530     }
   531   else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) {
   532     SI::PMT pmt(Data,false);
   533     if(pmt.CheckCRCAndParse()) {
   534       SI::PMT::Stream stream;
   535       for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
   536         if(stream.getStreamType()==0x05) {
   537           SI::CharArray data=stream.getData();
   538           if((data[1]&0xE0)==0xE0 && (data[3]&0xF0)==0xF0) {
   539             bool prvData=false, usrData=false;
   540             SI::Descriptor *d;
   541             for(SI::Loop::Iterator it; (d=stream.streamDescriptors.getNext(it)); ) {
   542               switch(d->getDescriptorTag()) {
   543                 case SI::PrivateDataSpecifierDescriptorTag:
   544                   d(printf("prv: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   545                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x000000be)
   546                     prvData=true;
   547                   break;
   548                 case 0x90:
   549                   d(printf("usr: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
   550                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x0000ffff)
   551                     usrData=true;
   552                   break;
   553                 default:
   554                   break;
   555                 }
   556               delete d;
   557               }
   558             if(prvData && usrData) {
   559               int pid=stream.getPid();
   560               d(printf("found citpid 0x%04x",pid))
   561               if(!Matches(pid,0xA0)) {
   562                 Add(pid,0xA0);
   563                 d(printf(" (added)"))
   564                 }
   565               d(printf("\n"))
   566               }
   567             }
   568           }
   569         }
   570       NextPmt(); pmtnext=0;
   571       }
   572     }
   573   else if(Tid==0xA0 && Source()) {
   574     SI::CIT cit(Data,false);
   575     if(cit.CheckCRCAndParse()) {
   576       cSchedulesLock SchedulesLock(true,10);
   577       cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock);
   578       if(Schedules) {
   579         int nCount=0;
   580         time_t firstTime=0;
   581         SI::Descriptor *d;
   582         int LanguagePreferenceShort=-1;
   583         int LanguagePreferenceExt=-1;
   584         bool UseExtendedEventDescriptor=false;
   585         SI::ExtendedEventDescriptors *ExtendedEventDescriptors=0;
   586         SI::ShortEventDescriptor *ShortEventDescriptor=0;
   587         char *order=0, *rating=0;
   588         for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
   589           switch(d->getDescriptorTag()) {
   590             case 0xF0: // order information
   591               if(SetupPE.OrderInfo) {
   592                 static const char *text[] = {
   593                   "Ordernumber",
   594                   "Price",
   595                   "Ordering",
   596                   "SMS",
   597                   "WWW"
   598                   };
   599                 char buff[512];
   600                 int p=0;
   601                 const unsigned char *data=d->getData().getData()+2;
   602                 for(int i=0; i<5; i++) {
   603                   int l=data[0]; 
   604                   if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %.*s",tr(text[i]),l,&data[1]);
   605                   data+=l+1;
   606                   }
   607                 if(p>0) order=strdup(buff);
   608                 }
   609               break;
   610             case 0xF1: // parental rating
   611               if(SetupPE.RatingInfo) {
   612                 char buff[512];
   613                 int p=0;
   614                 const unsigned char *data=d->getData().getData()+2;
   615                 p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %d %s",tr("Rating"),data[0]+3,tr("years"));
   616                 data+=7;
   617                 int l=data[0]; 
   618                 if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p," (%.*s)",l,&data[1]);
   619                 if(p>0) rating=strdup(buff);
   620                 }
   621               break;
   622             case 0xF2: // transmisions
   623               if(nCount>=0) {
   624                 nCount++;
   625                 cDescrF2 f2(d);
   626                 if(f2.Next()) {
   627                   if(nCount==1) firstTime=f2.StartTime();
   628                   else {
   629                     time_t time=f2.StartTime();
   630                     if(firstTime<time-5*50 || firstTime>time+5*60)
   631                       nCount=-1;
   632                     }
   633                   }
   634                 }
   635               break;
   636             case SI::ExtendedEventDescriptorTag:
   637               {
   638               SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d;
   639 #if VDRVERSNUM < 10332
   640               if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(eed->languageCode), LanguagePreferenceExt) || !ExtendedEventDescriptors) {
   641 #else
   642               if(I18nIsPreferredLanguage(Setup.EPGLanguages,eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
   643 #endif
   644                  delete ExtendedEventDescriptors;
   645                  ExtendedEventDescriptors=new SI::ExtendedEventDescriptors;
   646                  UseExtendedEventDescriptor=true;
   647                  }
   648               if(UseExtendedEventDescriptor) {
   649                  ExtendedEventDescriptors->Add(eed);
   650                  d=NULL; // so that it is not deleted
   651                  }
   652               if(eed->getDescriptorNumber()==eed->getLastDescriptorNumber())
   653                  UseExtendedEventDescriptor=false;
   654               }
   655               break;
   656             case SI::ShortEventDescriptorTag:
   657               {
   658               SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d;
   659 #if VDRVERSNUM < 10332
   660               if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(sed->languageCode), LanguagePreferenceShort) || !ShortEventDescriptor) {
   661 #else
   662               if(I18nIsPreferredLanguage(Setup.EPGLanguages,sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
   663 #endif
   664                  delete ShortEventDescriptor;
   665                  ShortEventDescriptor=sed;
   666                  d=NULL; // so that it is not deleted
   667                  }
   668               }
   669               break;
   670             default:
   671               break;
   672             }
   673           delete d;
   674           }
   675 
   676         bool Modified=false;
   677         int optCount=0;
   678         for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
   679           if(d->getDescriptorTag()==0xF2) {
   680             optCount++;
   681 
   682             cDescrF2 f2(d);
   683             tChannelID channelID(Source(),f2.OrgNetworkId(),f2.TransportStreamId(),f2.ServiceId());
   684             cChannel *channel=Channels.GetByChannelID(channelID,true);
   685             if(!channel) continue;
   686 
   687             cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
   688             if(!pSchedule) {
   689                pSchedule=new cSchedule(channelID);
   690                Schedules->Add(pSchedule);
   691                }
   692 
   693             for(f2.Start(); f2.Next();) {
   694               u_int16_t EventId=(cit.getContentId()<<4) | f2.Index();
   695               time_t StartTime=f2.StartTime();
   696 
   697               bool isOpt=false;
   698               if(f2.Index()==0 && nCount>1) isOpt=true;
   699 
   700               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))))
   701               if(StartTime+cit.getDuration()+Setup.EPGLinger*60<time(0)) {
   702                 d2(printf("(old)\n"))
   703                 continue;
   704                 }
   705 
   706               bool newEvent=false;
   707               cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,StartTime);
   708               if(!pEvent) {
   709                  d2(printf("(new)\n"))
   710 #if VDRVERSNUM >= 10325
   711                  pEvent=new cEvent(EventId);
   712 #else
   713                  pEvent=new cEvent(channelID,EventId);
   714 #endif
   715                  if(!pEvent) continue;
   716                  newEvent=true;
   717                  }
   718               else {
   719                  d2(printf("(upd)\n"))
   720                  pEvent->SetSeen();
   721                  if(pEvent->TableID()==0x00) continue;
   722                  if(Tid==pEvent->TableID() && pEvent->Version()==cit.getVersionNumber()) continue;
   723                  }
   724               pEvent->SetEventID(EventId);
   725               pEvent->SetTableID(Tid);
   726               pEvent->SetVersion(cit.getVersionNumber());
   727               pEvent->SetStartTime(StartTime);
   728               pEvent->SetDuration(cit.getDuration());
   729 
   730               if(ShortEventDescriptor) {
   731                 char buffer[256];
   732                 ShortEventDescriptor->name.getText(buffer,sizeof(buffer));
   733                 if(isOpt) {
   734                   char buffer2[sizeof(buffer)+32];
   735                   snprintf(buffer2,sizeof(buffer2),optPats[SetupPE.OptPat],buffer,optCount);
   736                   pEvent->SetTitle(buffer2);
   737                   }
   738                 else
   739                   pEvent->SetTitle(buffer);
   740                 pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer)));
   741                 }
   742               if(ExtendedEventDescriptors) {
   743                 char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ")+1];
   744                 pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer,sizeof(buffer),": "));
   745                 }
   746               if(order || rating) {
   747                 int len=(pEvent->Description() ? strlen(pEvent->Description()) : 0) +
   748                         (order                 ? strlen(order) : 0) +
   749                         (rating                ? strlen(rating) : 0);
   750                 char buffer[len+32];
   751                 buffer[0]=0;
   752                 if(pEvent->Description()) strcat(buffer,pEvent->Description());
   753                 if(rating)                strcat(buffer,rating);
   754                 if(order)                 strcat(buffer,order);
   755                 pEvent->SetDescription(buffer);
   756                 }
   757 
   758               if(newEvent) pSchedule->AddEvent(pEvent);
   759               pEvent->SetComponents(NULL);
   760               pEvent->FixEpgBugs();
   761               Modified=true;
   762               }
   763             if(Modified) {
   764               pSchedule->Sort();
   765               Schedules->SetModified(pSchedule);
   766               }
   767             }
   768           delete d;
   769           }
   770         delete ExtendedEventDescriptors;
   771         delete ShortEventDescriptor;
   772         free(order);
   773         free(rating);
   774         }
   775       }
   776     }
   777 }
   778 
   779 // --- cPluginPremiereEpg ------------------------------------------------------
   780 
   781 class cPluginPremiereEpg : public cPlugin {
   782 private:
   783   struct {
   784     cFilterPremiereEpg *filter;
   785     cDevice *device;
   786     } epg[MAXDVBDEVICES];
   787 public:
   788   cPluginPremiereEpg(void);
   789   virtual const char *Version(void) { return VERSION; }
   790   virtual const char *Description(void) { return tr(DESCRIPTION); }
   791   virtual bool Start(void);
   792   virtual void Stop(void);
   793   virtual cMenuSetupPage *SetupMenu(void);
   794   virtual bool SetupParse(const char *Name, const char *Value);
   795   };
   796 
   797 cPluginPremiereEpg::cPluginPremiereEpg(void)
   798 {
   799   memset(epg,0,sizeof(epg));
   800 }
   801 
   802 bool cPluginPremiereEpg::Start(void)
   803 {
   804   RegisterI18n(Phrases);
   805   for(int i=0; i<MAXDVBDEVICES; i++) {
   806     cDevice *dev=cDevice::GetDevice(i);
   807     if(dev) {
   808       epg[i].device=dev;
   809       dev->AttachFilter(epg[i].filter=new cFilterPremiereEpg);
   810       isyslog("Attached premiere EPG filter to device %d",i);
   811       }
   812     }
   813   return true;
   814 }
   815 
   816 void cPluginPremiereEpg::Stop(void)
   817 {
   818   for(int i=0; i<MAXDVBDEVICES; i++) {
   819     cDevice *dev=epg[i].device;
   820     if(dev) dev->Detach(epg[i].filter);
   821     delete epg[i].filter;
   822     epg[i].device=0;
   823     epg[i].filter=0;
   824     }
   825 }
   826 
   827 cMenuSetupPage *cPluginPremiereEpg::SetupMenu(void)
   828 {
   829   return new cMenuSetupPremiereEpg;
   830 }
   831 
   832 bool cPluginPremiereEpg::SetupParse(const char *Name, const char *Value)
   833 {
   834   if      (!strcasecmp(Name, "OptionPattern")) SetupPE.OptPat     = atoi(Value);
   835   else if (!strcasecmp(Name, "OrderInfo"))     SetupPE.OrderInfo  = atoi(Value);
   836   else if (!strcasecmp(Name, "RatingInfo"))    SetupPE.RatingInfo = atoi(Value);
   837   else return false;
   838   return true;
   839 }
   840 
   841 VDRPLUGINCREATOR(cPluginPremiereEpg); // Don't touch this!