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