Ticket #13517: mythfilldatabase-reduce-mem-usage1.diff

File mythfilldatabase-reduce-mem-usage1.diff, 48.2 KB (added by Peter Bennett, 5 years ago)

Proposed fix from OP

  • mythtv/mythtv/programs/mythfilldatabase/filldata.cpp

    diff -Naur build/mythtv/mythtv/programs/mythfilldatabase/filldata.cpp build3/mythtv/mythtv/programs/mythfilldatabase/filldata.cpp
    old new  
    9292    ChannelInfoList chanlist;
    9393    QMap<QString, QList<ProgInfo> > proglist;
    9494
    95     m_xmltvParser.lateInit();
    9695    if (!m_xmltvParser.parseFile(filename, &chanlist, &proglist))
    9796        return false;
    9897
  • mythtv/mythtv/programs/mythfilldatabase/xmltvparser.cpp

    diff -Naur build/mythtv/mythtv/programs/mythfilldatabase/xmltvparser.cpp build3/mythtv/mythtv/programs/mythfilldatabase/xmltvparser.cpp
    old new  
    3434    m_currentYear = MythDate::current().date().toString("yyyy").toUInt();
    3535}
    3636
    37 void XMLTVParser::lateInit()
    38 {
    39     m_movieGrabberPath = MetadataDownload::GetMovieGrabber();
    40     m_tvGrabberPath = MetadataDownload::GetTelevisionGrabber();
    41 }
    42 
    4337static uint ELFHash(const QByteArray &ba)
    4438{
    4539    const auto *k = (const uchar *)ba.data();
     
    6054    return h;
    6155}
    6256
    63 static QString getFirstText(const QDomElement& element)
    64 {
    65     for (QDomNode dname = element.firstChild(); !dname.isNull();
    66          dname = dname.nextSibling())
    67     {
    68         QDomText t = dname.toText();
    69         if (!t.isNull())
    70             return t.data();
    71     }
    72     return QString();
    73 }
    74 
    75 ChannelInfo *XMLTVParser::parseChannel(QDomElement &element, QUrl &baseUrl)
    76 {
    77     auto *chaninfo = new ChannelInfo;
    78 
    79     QString xmltvid = element.attribute("id", "");
    80 
    81     chaninfo->m_xmltvId = xmltvid;
    82     chaninfo->m_tvFormat = "Default";
    83 
    84     for (QDomNode child = element.firstChild(); !child.isNull();
    85          child = child.nextSibling())
    86     {
    87         QDomElement info = child.toElement();
    88         if (!info.isNull())
    89         {
    90             if (info.tagName() == "icon")
    91             {
    92                 if (chaninfo->m_icon.isEmpty())
    93                 {
    94                     QString path = info.attribute("src", "");
    95                     if (!path.isEmpty() && !path.contains("://"))
    96                     {
    97                         QString base = baseUrl.toString(QUrl::StripTrailingSlash);
    98                         chaninfo->m_icon = base +
    99                             ((path.startsWith("/")) ? path : QString("/") + path);
    100                     }
    101                     else if (!path.isEmpty())
    102                     {
    103                         QUrl url(path);
    104                         if (url.isValid())
    105                             chaninfo->m_icon = url.toString();
    106                     }
    107                 }
    108             }
    109             else if (info.tagName() == "display-name")
    110             {
    111                 if (chaninfo->m_name.isEmpty())
    112                 {
    113                     chaninfo->m_name = info.text();
    114                 }
    115                 else if (chaninfo->m_callSign.isEmpty())
    116                 {
    117                     chaninfo->m_callSign = info.text();
    118                 }
    119                 else if (chaninfo->m_chanNum.isEmpty())
    120                 {
    121                     chaninfo->m_chanNum = info.text();
    122                 }
    123             }
    124         }
    125     }
    126 
    127     chaninfo->m_freqId = chaninfo->m_chanNum;
    128     return chaninfo;
    129 }
    130 
    13157static void fromXMLTVDate(QString &timestr, QDateTime &dt)
    13258{
    13359    // The XMLTV spec requires dates to either be in UTC/GMT or to specify a
     
    223149
    224150    QDateTime tmpDT = QDateTime(tmpDate, tmpTime, Qt::UTC);
    225151    if (!tmpDT.isValid())
    226         {
    227             LOG(VB_XMLTV, LOG_ERR,
    228                 QString("Invalid datetime (combination of date/time) "
     152    {
     153        LOG(VB_XMLTV, LOG_ERR,
     154            QString("Invalid datetime (combination of date/time) "
    229155                    "in XMLTV data, ignoring: %1").arg(timestr));
    230             return;
    231         }
     156        return;
     157    }
    232158
    233159    // While this seems like a hack, it's better than what was done before
    234160    QString isoDateString = tmpDT.toString(Qt::ISODate);
     
    248174    timestr = MythDate::toString(dt, MythDate::kFilename);
    249175}
    250176
    251 static void parseCredits(QDomElement &element, ProgInfo *pginfo)
     177static int readNextWithErrorCheck(QXmlStreamReader &xml)
    252178{
    253     for (QDomNode child = element.firstChild(); !child.isNull();
    254          child = child.nextSibling())
    255     {
    256         QDomElement info = child.toElement();
    257         if (!info.isNull())
    258             pginfo->AddPerson(info.tagName(), getFirstText(info));
    259     }
    260 }
    261 
    262 static void parseVideo(QDomElement &element, ProgInfo *pginfo)
    263 {
    264     for (QDomNode child = element.firstChild(); !child.isNull();
    265          child = child.nextSibling())
    266     {
    267         QDomElement info = child.toElement();
    268         if (!info.isNull())
    269         {
    270             if (info.tagName() == "quality")
    271             {
    272                 if (getFirstText(info) == "HDTV")
    273                     pginfo->m_videoProps |= VID_HDTV;
    274             }
    275             else if (info.tagName() == "aspect")
    276             {
    277                 if (getFirstText(info) == "16:9")
    278                     pginfo->m_videoProps |= VID_WIDESCREEN;
    279             }
    280         }
    281     }
    282 }
    283 
    284 static void parseAudio(QDomElement &element, ProgInfo *pginfo)
    285 {
    286     for (QDomNode child = element.firstChild(); !child.isNull();
    287          child = child.nextSibling())
    288     {
    289         QDomElement info = child.toElement();
    290         if (!info.isNull())
    291         {
    292             if (info.tagName() == "stereo")
    293             {
    294                 if (getFirstText(info) == "mono")
    295                 {
    296                     pginfo->m_audioProps |= AUD_MONO;
    297                 }
    298                 else if (getFirstText(info) == "stereo")
    299                 {
    300                     pginfo->m_audioProps |= AUD_STEREO;
    301                 }
    302                 else if (getFirstText(info) == "dolby" ||
    303                         getFirstText(info) == "dolby digital")
    304                 {
    305                     pginfo->m_audioProps |= AUD_DOLBY;
    306                 }
    307                 else if (getFirstText(info) == "surround")
    308                 {
    309                     pginfo->m_audioProps |= AUD_SURROUND;
    310                 }
    311             }
    312         }
    313     }
    314 }
    315 
    316 ProgInfo *XMLTVParser::parseProgram(QDomElement &element)
    317 {
    318     QString programid;
    319     QString season;
    320     QString episode;
    321     QString totalepisodes;
    322     auto *pginfo = new ProgInfo();
    323 
    324     QString text = element.attribute("start", "");
    325     fromXMLTVDate(text, pginfo->m_starttime);
    326     pginfo->m_startts = text;
    327 
    328     text = element.attribute("stop", "");
    329     fromXMLTVDate(text, pginfo->m_endtime);
    330     pginfo->m_endts = text;
    331 
    332     text = element.attribute("channel", "");
    333     QStringList split = text.split(" ");
    334 
    335     pginfo->m_channel = split[0];
    336 
    337     text = element.attribute("clumpidx", "");
    338     if (!text.isEmpty())
    339     {
    340         split = text.split('/');
    341         pginfo->m_clumpidx = split[0];
    342         pginfo->m_clumpmax = split[1];
    343     }
    344 
    345     for (QDomNode child = element.firstChild(); !child.isNull();
    346          child = child.nextSibling())
    347     {
    348         QDomElement info = child.toElement();
    349         if (!info.isNull())
    350         {
    351             if (info.tagName() == "title")
    352             {
    353                 if (info.attribute("lang") == "ja_JP")
    354                 {   // NOLINT(bugprone-branch-clone)
    355                     pginfo->m_title = getFirstText(info);
    356                 }
    357                 else if (info.attribute("lang") == "ja_JP@kana")
    358                 {
    359                     pginfo->m_title_pronounce = getFirstText(info);
    360                 }
    361                 else if (pginfo->m_title.isEmpty())
    362                 {
    363                     pginfo->m_title = getFirstText(info);
    364                 }
    365             }
    366             else if (info.tagName() == "sub-title" &&
    367                      pginfo->m_subtitle.isEmpty())
    368             {
    369                 pginfo->m_subtitle = getFirstText(info);
    370             }
    371             else if (info.tagName() == "desc" && pginfo->m_description.isEmpty())
    372             {
    373                 pginfo->m_description = getFirstText(info);
    374             }
    375             else if (info.tagName() == "category")
    376             {
    377                 const QString cat = getFirstText(info);
    378 
    379                 if (ProgramInfo::kCategoryNone == pginfo->m_categoryType &&
    380                     string_to_myth_category_type(cat) != ProgramInfo::kCategoryNone)
    381                 {
    382                     pginfo->m_categoryType = string_to_myth_category_type(cat);
    383                 }
    384                 else if (pginfo->m_category.isEmpty())
    385                 {
    386                     pginfo->m_category = cat;
    387                 }
    388 
    389                 if ((cat.compare(QObject::tr("movie"),Qt::CaseInsensitive) == 0) ||
    390                     (cat.compare(QObject::tr("film"),Qt::CaseInsensitive) == 0))
    391                 {
    392                     // Hack for tv_grab_uk_rt
    393                     pginfo->m_categoryType = ProgramInfo::kCategoryMovie;
    394                 }
    395 
    396                 pginfo->m_genres.append(cat);
    397             }
    398             else if (info.tagName() == "date" && (pginfo->m_airdate == 0U))
    399             {
    400                 // Movie production year
    401                 QString date = getFirstText(info);
    402                 pginfo->m_airdate = date.left(4).toUInt();
    403             }
    404             else if (info.tagName() == "star-rating" && pginfo->m_stars == 0.0F)
    405             {
    406                 QDomNodeList values = info.elementsByTagName("value");
    407                 QDomElement item;
    408                 QString stars;
    409                 float rating = 0.0;
    410 
    411                 // Use the first rating to appear in the xml, this should be
    412                 // the most important one.
    413                 //
    414                 // Averaging is not a good idea here, any subsequent ratings
    415                 // are likely to represent that days recommended programmes
    416                 // which on a bad night could given to an average programme.
    417                 // In the case of uk_rt it's not unknown for a recommendation
    418                 // to be given to programmes which are 'so bad, you have to
    419                 // watch!'
    420                 //
    421                 // XMLTV uses zero based ratings and signals no rating by absence.
    422                 // A rating from 1 to 5 is encoded as 0/4 to 4/4.
    423                 // MythTV uses zero to signal no rating!
    424                 // The same rating is encoded as 0.2 to 1.0 with steps of 0.2, it
    425                 // is not encoded as 0.0 to 1.0 with steps of 0.25 because
    426                 // 0 signals no rating!
    427                 // See http://xmltv.cvs.sourceforge.net/viewvc/xmltv/xmltv/xmltv.dtd?revision=1.47&view=markup#l539
    428                 item = values.item(0).toElement();
    429                 if (!item.isNull())
    430                 {
    431                     stars = getFirstText(item);
    432                     float num = stars.section('/', 0, 0).toFloat() + 1;
    433                     float den = stars.section('/', 1, 1).toFloat() + 1;
    434                     if (0.0F < den)
    435                         rating = num/den;
    436                 }
    437 
    438                 pginfo->m_stars = rating;
    439             }
    440             else if (info.tagName() == "rating")
    441             {
    442                 // again, the structure of ratings seems poorly represented
    443                 // in the XML.  no idea what we'd do with multiple values.
    444                 QDomNodeList values = info.elementsByTagName("value");
    445                 QDomElement item = values.item(0).toElement();
    446                 if (item.isNull())
    447                     continue;
    448                 EventRating rating;
    449                 rating.m_system = info.attribute("system", "");
    450                 rating.m_rating = getFirstText(item);
    451                 pginfo->m_ratings.append(rating);
    452             }
    453             else if (info.tagName() == "previously-shown")
    454             {
    455                 pginfo->m_previouslyshown = true;
    456 
    457                 QString prevdate = info.attribute("start");
    458                 if (!prevdate.isEmpty())
    459                 {
    460                     QDateTime date;
    461                     fromXMLTVDate(prevdate, date);
    462                     pginfo->m_originalairdate = date.date();
    463                 }
    464             }
    465             else if (info.tagName() == "credits")
    466             {
    467                 parseCredits(info, pginfo);
    468             }
    469             else if (info.tagName() == "subtitles")
    470             {
    471                 if (info.attribute("type") == "teletext")
    472                     pginfo->m_subtitleType |= SUB_NORMAL;
    473                 else if (info.attribute("type") == "onscreen")
    474                     pginfo->m_subtitleType |= SUB_ONSCREEN;
    475                 else if (info.attribute("type") == "deaf-signed")
    476                     pginfo->m_subtitleType |= SUB_SIGNED;
    477             }
    478             else if (info.tagName() == "audio")
    479             {
    480                 parseAudio(info, pginfo);
    481             }
    482             else if (info.tagName() == "video")
    483             {
    484                 parseVideo(info, pginfo);
    485             }
    486             else if (info.tagName() == "episode-num")
    487             {
    488                 if (info.attribute("system") == "dd_progid")
    489                 {
    490                     QString episodenum(getFirstText(info));
    491                     // if this field includes a dot, strip it out
    492                     int idx = episodenum.indexOf('.');
    493                     if (idx != -1)
    494                         episodenum.remove(idx, 1);
    495                     programid = episodenum;
    496                     /* Only EPisodes and SHows are part of a series for SD */
    497                     if (programid.startsWith(QString("EP")) ||
    498                         programid.startsWith(QString("SH")))
    499                         pginfo->m_seriesId = QString("EP") + programid.mid(2,8);
    500                 }
    501                 else if (info.attribute("system") == "xmltv_ns")
    502                 {
    503                     QString episodenum(getFirstText(info));
    504                     episode = episodenum.section('.',1,1);
    505                     totalepisodes = episode.section('/',1,1).trimmed();
    506                     episode = episode.section('/',0,0).trimmed();
    507                     season = episodenum.section('.',0,0).trimmed();
    508                     season = season.section('/',0,0).trimmed();
    509                     QString part(episodenum.section('.',2,2));
    510                     QString partnumber(part.section('/',0,0).trimmed());
    511                     QString parttotal(part.section('/',1,1).trimmed());
    512 
    513                     pginfo->m_categoryType = ProgramInfo::kCategorySeries;
    514 
    515                     if (!season.isEmpty())
    516                     {
    517                         int tmp = season.toUInt() + 1;
    518                         pginfo->m_season = tmp;
    519                         season = QString::number(tmp);
    520                         pginfo->m_syndicatedepisodenumber = 'S' + season;
    521                     }
    522 
    523                     if (!episode.isEmpty())
    524                     {
    525                         int tmp = episode.toUInt() + 1;
    526                         pginfo->m_episode = tmp;
    527                         episode = QString::number(tmp);
    528                         pginfo->m_syndicatedepisodenumber.append('E' + episode);
    529                     }
    530 
    531                     if (!totalepisodes.isEmpty())
    532                     {
    533                         pginfo->m_totalepisodes = totalepisodes.toUInt();
    534                     }
    535 
    536                     uint partno = 0;
    537                     if (!partnumber.isEmpty())
    538                     {
    539                         bool ok = false;
    540                         partno = partnumber.toUInt(&ok) + 1;
    541                         partno = (ok) ? partno : 0;
    542                     }
    543 
    544                     if (!parttotal.isEmpty() && partno > 0)
    545                     {
    546                         bool ok = false;
    547                         uint partto = parttotal.toUInt(&ok);
    548                         if (ok && partnumber <= parttotal)
    549                         {
    550                             pginfo->m_parttotal  = partto;
    551                             pginfo->m_partnumber = partno;
    552                         }
    553                     }
    554                 }
    555                 else if (info.attribute("system") == "onscreen")
    556                 {
    557                     pginfo->m_categoryType = ProgramInfo::kCategorySeries;
    558                     if (pginfo->m_subtitle.isEmpty())
    559                     {
    560                         pginfo->m_subtitle = getFirstText(info);
    561                     }
    562                 }
    563                 else if ((info.attribute("system") == "themoviedb.org") &&
    564                     (m_movieGrabberPath.endsWith(QString("/tmdb3.py"))))
    565                 {
    566                     /* text is movie/<inetref> */
    567                     QString inetrefRaw(getFirstText(info));
    568                     if (inetrefRaw.startsWith(QString("movie/"))) {
    569                         QString inetref(QString ("tmdb3.py_") + inetrefRaw.section('/',1,1).trimmed());
    570                         pginfo->m_inetref = inetref;
    571                     }
    572                 }
    573                 else if ((info.attribute("system") == "thetvdb.com") &&
    574                     (m_tvGrabberPath.endsWith(QString("/ttvdb.py"))))
    575                 {
    576                     /* text is series/<inetref> */
    577                     QString inetrefRaw(getFirstText(info));
    578                     if (inetrefRaw.startsWith(QString("series/"))) {
    579                         QString inetref(QString ("ttvdb.py_") + inetrefRaw.section('/',1,1).trimmed());
    580                         pginfo->m_inetref = inetref;
    581                         /* ProgInfo does not have a collectionref, so we don't set any */
    582                     }
    583                 }
    584             }
    585         }
    586     }
    587 
    588     if (pginfo->m_category.isEmpty() &&
    589         pginfo->m_categoryType != ProgramInfo::kCategoryNone)
    590         pginfo->m_category = myth_category_type_to_string(pginfo->m_categoryType);
    591 
    592     if (!pginfo->m_airdate
    593         && ProgramInfo::kCategorySeries != pginfo->m_categoryType)
    594         pginfo->m_airdate = m_currentYear;
    595 
    596     if (programid.isEmpty())
    597     {
    598 
    599         /* Let's build ourself a programid */
    600 
    601         if (ProgramInfo::kCategoryMovie == pginfo->m_categoryType)
    602             programid = "MV";
    603         else if (ProgramInfo::kCategorySeries == pginfo->m_categoryType)
    604             programid = "EP";
    605         else if (ProgramInfo::kCategorySports == pginfo->m_categoryType)
    606             programid = "SP";
    607         else
    608             programid = "SH";
    609 
    610         QString seriesid = QString::number(ELFHash(pginfo->m_title.toUtf8()));
    611         pginfo->m_seriesId = seriesid;
    612         programid.append(seriesid);
    613 
    614         if (!episode.isEmpty() && !season.isEmpty())
    615         {
    616             /* Append unpadded episode and season number to the seriesid (to
    617                maintain consistency with historical encoding), but limit the
    618                season number representation to a single base-36 character to
    619                ensure unique programid generation. */
    620             int season_int = season.toInt();
    621             if (season_int > 35)
    622             {
    623                 // Cannot represent season as a single base-36 character, so
    624                 // remove the programid and fall back to normal dup matching.
    625                 if (ProgramInfo::kCategoryMovie != pginfo->m_categoryType)
    626                     programid.clear();
    627             }
    628             else
    629             {
    630                 programid.append(episode);
    631                 programid.append(QString::number(season_int, 36));
    632                 if (pginfo->m_partnumber && pginfo->m_parttotal)
    633                 {
    634                     programid += QString::number(pginfo->m_partnumber);
    635                     programid += QString::number(pginfo->m_parttotal);
    636                 }
    637             }
    638         }
    639         else
    640         {
    641             /* No ep/season info? Well then remove the programid and rely on
    642                normal dupchecking methods instead. */
    643             if (ProgramInfo::kCategoryMovie != pginfo->m_categoryType)
    644                 programid.clear();
    645         }
     179    xml.readNext();
     180    if (xml.hasError()) {
     181        LOG(VB_GENERAL, LOG_ERR, QString("Malformed XML file at line %1, %2").arg(xml.lineNumber()).arg(xml.errorString()));
     182        return false;
    646183    }
    647 
    648     pginfo->m_programId = programid;
    649 
    650     return pginfo;
     184    return true;
    651185}
    652186
    653187bool XMLTVParser::parseFile(
    654188    const QString& filename, ChannelInfoList *chanlist,
    655189    QMap<QString, QList<ProgInfo> > *proglist)
    656190{
    657     QDomDocument doc;
     191    m_movieGrabberPath = MetadataDownload::GetMovieGrabber();
     192    m_tvGrabberPath = MetadataDownload::GetTelevisionGrabber();
    658193    QFile f;
    659 
    660194    if (!dash_open(f, filename, QIODevice::ReadOnly))
    661195    {
    662196        LOG(VB_GENERAL, LOG_ERR,
     
    664198        return false;
    665199    }
    666200
    667     QString errorMsg = "unknown";
    668     int errorLine = 0;
    669     int errorColumn = 0;
    670 
    671     if (!doc.setContent(&f, &errorMsg, &errorLine, &errorColumn))
    672     {
    673         LOG(VB_GENERAL, LOG_ERR, QString("Error in %1:%2: %3")
    674             .arg(errorLine).arg(errorColumn).arg(errorMsg));
    675 
    676         f.close();
    677         return true;
    678     }
    679 
    680     f.close();
    681 
    682     QDomElement docElem = doc.documentElement();
    683 
    684     QUrl baseUrl(docElem.attribute("source-data-url", ""));
    685     //QUrl sourceUrl(docElem.attribute("source-info-url", ""));
    686 
     201    QXmlStreamReader xml(&f);
     202    QUrl baseUrl;
     203    QUrl sourceUrl;
    687204    QString aggregatedTitle;
    688205    QString aggregatedDesc;
     206    while (!xml.atEnd() && !xml.hasError()) {
     207        if (xml.readNextStartElement()) {
     208            if (xml.name() == "tv") {
     209                sourceUrl = QUrl(xml.attributes().value("source-info-url").toString());
     210                baseUrl = QUrl(xml.attributes().value("source-data-url").toString());
     211            }
     212            if (xml.name() == "channel") {
     213                //get id attribute
     214                QString xmltvid;
     215                xmltvid = xml.attributes().value( "id").toString();
     216                auto *chaninfo = new ChannelInfo;
     217                chaninfo->m_xmltvId = xmltvid;
     218                chaninfo->m_tvFormat = "Default";
     219
     220                //readNextStartElement says it reads for the next start element WITHIN the current element; but it doesnt; so we use readNext()
     221                do {
     222                    if (!readNextWithErrorCheck(xml))
     223                        return false;
     224                    if (xml.name() == "icon") {
     225                        if (chaninfo->m_icon.isEmpty()) {
     226                            QString path = xml.attributes().value("src").toString();
     227                            if (!path.isEmpty() && !path.contains("://")) {
     228                                QString base = baseUrl.toString(QUrl::StripTrailingSlash);
     229                                chaninfo->m_icon = base +
     230                                                   ((path.startsWith("/")) ? path : QString("/") + path);
     231                            }
     232                            else if (!path.isEmpty()) {
     233                                QUrl url(path);
     234                                if (url.isValid())
     235                                    chaninfo->m_icon = url.toString();
     236                            }
     237                        }
     238                    }
     239                    else if (xml.name() == "display-name") {
     240                        //now get text
     241                        QString text;
     242                        text = xml.readElementText(QXmlStreamReader::SkipChildElements);
     243                        if (!text.isEmpty()) {
     244                            if (chaninfo->m_name.isEmpty()) {
     245                                chaninfo->m_name = text;
     246                            }
     247                            else if (chaninfo->m_callSign.isEmpty()) {
     248                                chaninfo->m_callSign = text;
     249                            }
     250                            else if (chaninfo->m_chanNum.isEmpty()) {
     251                                chaninfo->m_chanNum = text;
     252                            }
     253                        }
     254                    }
     255                }
     256                while (! (xml.isEndElement() && xml.name() == "channel"));
     257                chaninfo->m_freqId = chaninfo->m_chanNum;
     258                //TODO optimize this, no use to do al this parsing if xmltvid is empty; but make sure you will read until the next channel!!
     259                if (!chaninfo->m_xmltvId.isEmpty())
     260                    chanlist->push_back(*chaninfo);
     261                delete chaninfo;
     262            }//channel
     263            else if (xml.name() == "programme") {
     264                QString programid, season, episode, totalepisodes;
     265                auto *pginfo = new ProgInfo();
     266
     267                QString text = xml.attributes().value("start").toString();
     268                fromXMLTVDate(text, pginfo->m_starttime);
     269                pginfo->m_startts = text;
     270
     271                text = xml.attributes().value("stop").toString();
     272                fromXMLTVDate(text, pginfo->m_endtime);
     273                pginfo->m_endts = text;
     274
     275                text = xml.attributes().value("channel").toString();
     276                QStringList split = text.split(" ");
     277                pginfo->m_channel = split[0];
     278
     279                text = xml.attributes().value("clumpidx").toString();
     280                if (!text.isEmpty()) {
     281                    split = text.split('/');
     282                    pginfo->m_clumpidx = split[0];
     283                    pginfo->m_clumpmax = split[1];
     284                }
     285
     286                do {
     287                    if (!readNextWithErrorCheck(xml))
     288                        return false;
     289                    if (xml.name() == "title") {
     290                        QString text2=xml.readElementText(QXmlStreamReader::SkipChildElements);
     291                        if (xml.attributes().value("lang").toString() == "ja_JP") {
     292                            pginfo->m_title = text2;
     293                        }
     294                        else if (xml.attributes().value("lang").toString() == "ja_JP@kana") {
     295                            pginfo->m_title_pronounce = text2;
     296                        }
     297                        else if (pginfo->m_title.isEmpty()) {
     298                            pginfo->m_title = text2;
     299                        }
     300                    }
     301                    else if (xml.name() == "sub-title" &&  pginfo->m_subtitle.isEmpty()) {
     302                        pginfo->m_subtitle = xml.readElementText(QXmlStreamReader::SkipChildElements);
     303                    }
     304                    else if (xml.name() == "subtitles") {
     305                        if (xml.attributes().value("type").toString() == "teletext")
     306                            pginfo->m_subtitleType |= SUB_NORMAL;
     307                        else if (xml.attributes().value("type").toString() == "onscreen")
     308                            pginfo->m_subtitleType |= SUB_ONSCREEN;
     309                        else if (xml.attributes().value("type").toString() == "deaf-signed")
     310                            pginfo->m_subtitleType |= SUB_SIGNED;
     311                    }
     312                    else if (xml.name() == "desc" && pginfo->m_description.isEmpty()) {
     313                        pginfo->m_description = xml.readElementText(QXmlStreamReader::SkipChildElements);
     314                    }
     315                    else if (xml.name() == "category") {
     316                        const QString cat = xml.readElementText(QXmlStreamReader::SkipChildElements);
    689317
    690     QDomNode n = docElem.firstChild();
    691     while (!n.isNull())
    692     {
    693         QDomElement e = n.toElement();
    694         if (!e.isNull())
    695         {
    696             if (e.tagName() == "channel")
    697             {
    698                 ChannelInfo *chinfo = parseChannel(e, baseUrl);
    699                 if (!chinfo->m_xmltvId.isEmpty())
    700                     chanlist->push_back(*chinfo);
    701                 delete chinfo;
    702             }
    703             else if (e.tagName() == "programme")
    704             {
    705                 ProgInfo *pginfo = parseProgram(e);
     318                        if (ProgramInfo::kCategoryNone == pginfo->m_categoryType && string_to_myth_category_type(cat) != ProgramInfo::kCategoryNone) {
     319                            pginfo->m_categoryType = string_to_myth_category_type(cat);
     320                        }
     321                        else if (pginfo->m_category.isEmpty()) {
     322                            pginfo->m_category = cat;
     323                        }
     324                        if ((cat.compare(QObject::tr("movie"),Qt::CaseInsensitive) == 0) || (cat.compare(QObject::tr("film"),Qt::CaseInsensitive) == 0)) {
     325                            // Hack for tv_grab_uk_rt
     326                            pginfo->m_categoryType = ProgramInfo::kCategoryMovie;
     327                        }
     328                        pginfo->m_genres.append(cat);
     329                    }
     330                    else if (xml.name() == "date" && (pginfo->m_airdate == 0U)) {
     331                        // Movie production year
     332                        QString date = xml.readElementText(QXmlStreamReader::SkipChildElements);
     333                        pginfo->m_airdate = date.left(4).toUInt();
     334                    }
     335                    else if (xml.name() == "star-rating") {
     336                        QString stars;
     337                        float rating = 0.0;
     338
     339                        // Use the first rating to appear in the xml, this should be
     340                        // the most important one.
     341                        //
     342                        // Averaging is not a good idea here, any subsequent ratings
     343                        // are likely to represent that days recommended programmes
     344                        // which on a bad night could given to an average programme.
     345                        // In the case of uk_rt it's not unknown for a recommendation
     346                        // to be given to programmes which are 'so bad, you have to
     347                        // watch!'
     348                        //
     349                        // XMLTV uses zero based ratings and signals no rating by absence.
     350                        // A rating from 1 to 5 is encoded as 0/4 to 4/4.
     351                        // MythTV uses zero to signal no rating!
     352                        // The same rating is encoded as 0.2 to 1.0 with steps of 0.2, it
     353                        // is not encoded as 0.0 to 1.0 with steps of 0.25 because
     354                        // 0 signals no rating!
     355                        // See http://xmltv.cvs.sourceforge.net/viewvc/xmltv/xmltv/xmltv.dtd?revision=1.47&view=markup#l539
     356                        stars = "0"; //no rating
     357                        do {
     358                            if (!readNextWithErrorCheck(xml))
     359                                return false;
     360                            if (xml.isStartElement()) {
     361                                if (xml.name() == "value") {
     362                                    stars=xml.readElementText(QXmlStreamReader::SkipChildElements);
     363                                }
     364                            }
     365                        }
     366                        while (! (xml.isEndElement() && xml.name() == "star-rating"));
     367                        if (pginfo->m_stars == 0.0F) {
     368                            float num = stars.section('/', 0, 0).toFloat() + 1;
     369                            float den = stars.section('/', 1, 1).toFloat() + 1;
     370                            if (0.0F < den)
     371                                rating = num/den;
     372                        }
     373                        pginfo->m_stars = rating;
     374                    }
     375                    else if (xml.name() == "rating") {
     376                        // again, the structure of ratings seems poorly represented
     377                        // in the XML.  no idea what we'd do with multiple values.
     378                        QString rat;
     379                        QString rating_system = xml.attributes().value("system").toString();
     380                                                                                                if (rating_system == NULL)
     381                                                                                                    rating_system = "";
     382
     383                        do {
     384                            if (!readNextWithErrorCheck(xml))
     385                                return false;
     386                            if (xml.isStartElement()) {
     387                                if (xml.name() == "value") {
     388                                    rat=xml.readElementText(QXmlStreamReader::SkipChildElements);
     389                                }
     390                            }
     391                        }
     392                        while (! (xml.isEndElement() && xml.name() == "rating"));
     393                                                                                               
     394                        if (!rat.isEmpty()) {
     395                            EventRating rating;
     396                            rating.m_system = rating_system;
     397                            rating.m_rating = rat;
     398                            pginfo->m_ratings.append(rating);
     399                        }
     400                    }
     401                    else if (xml.name() == "previously-shown") {
     402                        pginfo->m_previouslyshown = true;
     403                        QString prevdate = xml.attributes().value( "start").toString();
     404                        if (!prevdate.isEmpty()) {
     405                            QDateTime date;
     406                            fromXMLTVDate(prevdate, date);
     407                            pginfo->m_originalairdate = date.date();
     408                        }
     409                    }
     410                    else if (xml.name() == "credits") {
     411                        do {
     412                            if (!readNextWithErrorCheck(xml))
     413                                return false;
     414                            if (xml.isStartElement()) {
     415                                QString tagname=xml.name().toString();
     416                                QString text2=xml.readElementText(QXmlStreamReader::SkipChildElements);
     417                                pginfo->AddPerson(tagname, text2);
     418                            }
     419                        }
     420                        while (! (xml.isEndElement() && xml.name() == "credits"));
     421                    }
     422                    else if (xml.name() == "audio") {
     423                        do {
     424                            if (!readNextWithErrorCheck(xml))
     425                                return false;
     426                            if (xml.isStartElement()) {
     427                                if (xml.name() == "stereo") {
     428                                    QString text2=xml.readElementText(QXmlStreamReader::SkipChildElements);
     429                                    if (text2 == "mono") {
     430                                        pginfo->m_audioProps |= AUD_MONO;
     431                                    }
     432                                    else if (text2 == "stereo") {
     433                                        pginfo->m_audioProps |= AUD_STEREO;
     434                                    }
     435                                    else if (text2 == "dolby" || text2 == "dolby digital") {
     436                                        pginfo->m_audioProps |= AUD_DOLBY;
     437                                    }
     438                                    else if (text2 == "surround") {
     439                                        pginfo->m_audioProps |= AUD_SURROUND;
     440                                    }
     441                                }
     442                            }
     443                        }
     444                        while (! (xml.isEndElement() && xml.name() == "audio"));
     445                    }
     446                    else if (xml.name() == "video") {
     447                        do {
     448                            if (!readNextWithErrorCheck(xml))
     449                                return false;
     450                            if (xml.isStartElement()) {
     451                                if (xml.name() == "quality") {
     452                                    if (xml.readElementText(QXmlStreamReader::SkipChildElements) == "HDTV")
     453                                        pginfo->m_videoProps |= VID_HDTV;
     454                                }
     455                                else if (xml.name() == "aspect") {
     456                                    if (xml.readElementText(QXmlStreamReader::SkipChildElements) == "16:9")
     457                                        pginfo->m_videoProps |= VID_WIDESCREEN;
     458                                }
     459                            }
     460                        }
     461                        while (! (xml.isEndElement() && xml.name() == "video"));
     462                    }
     463                    else if (xml.name() == "episode-num") {
     464                        QString system = xml.attributes().value( "system").toString();
     465                        if (system == "dd_progid") {
     466                            QString episodenum(xml.readElementText(QXmlStreamReader::SkipChildElements));
     467                            // if this field includes a dot, strip it out
     468                            int idx = episodenum.indexOf('.');
     469                            if (idx != -1)
     470                                episodenum.remove(idx, 1);
     471                            programid = episodenum;
     472                            // Only EPisodes and SHows are part of a series for SD
     473                            if (programid.startsWith(QString("EP")) ||
     474                                    programid.startsWith(QString("SH")))
     475                                pginfo->m_seriesId = QString("EP") + programid.mid(2,8);
     476                        }
     477                        else if (system == "xmltv_ns") {
     478                            QString episodenum(xml.readElementText(QXmlStreamReader::SkipChildElements));
     479                            episode = episodenum.section('.',1,1);
     480                            totalepisodes = episode.section('/',1,1).trimmed();
     481                            episode = episode.section('/',0,0).trimmed();
     482                            season = episodenum.section('.',0,0).trimmed();
     483                            season = season.section('/',0,0).trimmed();
     484                            QString part(episodenum.section('.',2,2));
     485                            QString partnumber(part.section('/',0,0).trimmed());
     486                            QString parttotal(part.section('/',1,1).trimmed());
     487                            pginfo->m_categoryType = ProgramInfo::kCategorySeries;
     488                            if (!season.isEmpty()) {
     489                                int tmp = season.toUInt() + 1;
     490                                pginfo->m_season = tmp;
     491                                season = QString::number(tmp);
     492                                pginfo->m_syndicatedepisodenumber = 'S' + season;
     493                            }
     494                            if (!episode.isEmpty()) {
     495                                int tmp = episode.toUInt() + 1;
     496                                pginfo->m_episode = tmp;
     497                                episode = QString::number(tmp);
     498                                pginfo->m_syndicatedepisodenumber.append('E' + episode);
     499                            }
     500                            if (!totalepisodes.isEmpty()) {
     501                                pginfo->m_totalepisodes = totalepisodes.toUInt();
     502                            }
     503                            uint partno = 0;
     504                            if (!partnumber.isEmpty()) {
     505                                bool ok = false;
     506                                partno = partnumber.toUInt(&ok) + 1;
     507                                partno = (ok) ? partno : 0;
     508                            }
     509                            if (!parttotal.isEmpty() && partno > 0) {
     510                                bool ok = false;
     511                                uint partto = parttotal.toUInt(&ok);
     512                                if (ok && partnumber <= parttotal) {
     513                                    pginfo->m_parttotal  = partto;
     514                                    pginfo->m_partnumber = partno;
     515                                }
     516                            }
     517                        }
     518                        else if (system == "onscreen") {
     519                            pginfo->m_categoryType = ProgramInfo::kCategorySeries;
     520                            if (pginfo->m_subtitle.isEmpty()) {
     521                                pginfo->m_subtitle = xml.readElementText(QXmlStreamReader::SkipChildElements);
     522                            }
     523                        }
     524                        else if ((system == "themoviedb.org") &&  (m_movieGrabberPath.endsWith(QString("/tmdb3.py")))) {
     525                            // text is movie/<inetref>
     526                            QString inetrefRaw(xml.readElementText(QXmlStreamReader::SkipChildElements));
     527                            if (inetrefRaw.startsWith(QString("movie/"))) {
     528                                QString inetref(QString ("tmdb3.py_") + inetrefRaw.section('/',1,1).trimmed());
     529                                pginfo->m_inetref = inetref;
     530                            }
     531                        }
     532                        else if ((system == "thetvdb.com") && (m_tvGrabberPath.endsWith(QString("/ttvdb.py")))) {
     533                            // text is series/<inetref>
     534                            QString inetrefRaw(xml.readElementText(QXmlStreamReader::SkipChildElements));
     535                            if (inetrefRaw.startsWith(QString("series/"))) {
     536                                QString inetref(QString ("ttvdb.py_") + inetrefRaw.section('/',1,1).trimmed());
     537                                pginfo->m_inetref = inetref;
     538                                // ProgInfo does not have a collectionref, so we don't set any
     539                            }
     540                        }
     541                    }//episode-num
     542                }
     543                while (! (xml.isEndElement() && xml.name() == "programme"));
     544
     545                if (pginfo->m_category.isEmpty() && pginfo->m_categoryType != ProgramInfo::kCategoryNone)
     546                    pginfo->m_category = myth_category_type_to_string(pginfo->m_categoryType);
     547
     548                if (!pginfo->m_airdate && ProgramInfo::kCategorySeries != pginfo->m_categoryType)
     549                    pginfo->m_airdate = m_currentYear;
     550
     551                if (programid.isEmpty()) {
     552                    //Let's build ourself a programid
     553                    if (ProgramInfo::kCategoryMovie == pginfo->m_categoryType)
     554                        programid = "MV";
     555                    else if (ProgramInfo::kCategorySeries == pginfo->m_categoryType)
     556                        programid = "EP";
     557                    else if (ProgramInfo::kCategorySports == pginfo->m_categoryType)
     558                        programid = "SP";
     559                    else
     560                        programid = "SH";
    706561
    707                 if (!(pginfo->m_starttime.isValid()))
    708                 {
    709                     LOG(VB_GENERAL, LOG_WARNING, QString("Invalid programme (%1), "
    710                                                         "invalid start time, "
    711                                                         "skipping")
    712                                                         .arg(pginfo->m_title));
     562                    QString seriesid = QString::number(ELFHash(pginfo->m_title.toUtf8()));
     563                    pginfo->m_seriesId = seriesid;
     564                    programid.append(seriesid);
     565
     566                    if (!episode.isEmpty() && !season.isEmpty()) {
     567                        /* Append unpadded episode and season number to the seriesid (to
     568                           maintain consistency with historical encoding), but limit the
     569                           season number representation to a single base-36 character to
     570                           ensure unique programid generation. */
     571                        int season_int = season.toInt();
     572                        if (season_int > 35) {
     573                            // Cannot represent season as a single base-36 character, so
     574                            // remove the programid and fall back to normal dup matching.
     575                            if (ProgramInfo::kCategoryMovie != pginfo->m_categoryType)
     576                                programid.clear();
     577                        }
     578                        else {
     579                            programid.append(episode);
     580                            programid.append(QString::number(season_int, 36));
     581                            if (pginfo->m_partnumber && pginfo->m_parttotal) {
     582                                programid += QString::number(pginfo->m_partnumber);
     583                                programid += QString::number(pginfo->m_parttotal);
     584                            }
     585                        }
     586                    }
     587                    else {
     588                        /* No ep/season info? Well then remove the programid and rely on
     589                           normal dupchecking methods instead. */
     590                        if (ProgramInfo::kCategoryMovie != pginfo->m_categoryType)
     591                            programid.clear();
     592                    }
    713593                }
    714                 else if (pginfo->m_channel.isEmpty())
    715                 {
    716                     LOG(VB_GENERAL, LOG_WARNING, QString("Invalid programme (%1), "
    717                                                         "missing channel, "
    718                                                         "skipping")
    719                                                         .arg(pginfo->m_title));
     594                pginfo->m_programId = programid;
     595                if (!(pginfo->m_starttime.isValid())) {
     596                    LOG(VB_GENERAL, LOG_WARNING, QString("Invalid programme (%1), " "invalid start time, " "skipping").arg(pginfo->m_title));
    720597                }
    721                 else if (pginfo->m_startts == pginfo->m_endts)
    722                 {
    723                     LOG(VB_GENERAL, LOG_WARNING, QString("Invalid programme (%1), "
    724                                                         "identical start and end "
    725                                                         "times, skipping")
    726                                                         .arg(pginfo->m_title));
     598                else if (pginfo->m_channel.isEmpty()) {
     599                    LOG(VB_GENERAL, LOG_WARNING, QString("Invalid programme (%1), " "missing channel, " "skipping").arg(pginfo->m_title));
    727600                }
    728                 else
    729                 {
     601                else if (pginfo->m_startts == pginfo->m_endts) {
     602                    LOG(VB_GENERAL, LOG_WARNING, QString("Invalid programme (%1), " "identical start and end " "times, skipping").arg(pginfo->m_title));
     603                }
     604                else {
    730605                    if (pginfo->m_clumpidx.isEmpty())
    731606                        (*proglist)[pginfo->m_channel].push_back(*pginfo);
    732                     else
    733                     {
     607                    else {
    734608                        /* append all titles/descriptions from one clump */
    735                         if (pginfo->m_clumpidx.toInt() == 0)
    736                         {
     609                        if (pginfo->m_clumpidx.toInt() == 0) {
    737610                            aggregatedTitle.clear();
    738611                            aggregatedDesc.clear();
    739612                        }
    740 
    741                         if (!pginfo->m_title.isEmpty())
    742                         {
     613                        if (!pginfo->m_title.isEmpty()) {
    743614                            if (!aggregatedTitle.isEmpty())
    744615                                aggregatedTitle.append(" | ");
    745616                            aggregatedTitle.append(pginfo->m_title);
    746617                        }
    747 
    748                         if (!pginfo->m_description.isEmpty())
    749                         {
     618                        if (!pginfo->m_description.isEmpty()) {
    750619                            if (!aggregatedDesc.isEmpty())
    751620                                aggregatedDesc.append(" | ");
    752621                            aggregatedDesc.append(pginfo->m_description);
    753622                        }
    754                         if (pginfo->m_clumpidx.toInt() ==
    755                             pginfo->m_clumpmax.toInt() - 1)
    756                         {
     623                        if (pginfo->m_clumpidx.toInt() == pginfo->m_clumpmax.toInt() - 1) {
    757624                            pginfo->m_title = aggregatedTitle;
    758625                            pginfo->m_description = aggregatedDesc;
    759626                            (*proglist)[pginfo->m_channel].push_back(*pginfo);
     
    761628                    }
    762629                }
    763630                delete pginfo;
    764             }
    765         }
    766         n = n.nextSibling();
    767     }
     631            }//if programme
     632        }//if readNextStartElement
     633    }//while loop
     634    //TODO add code for adding data on the run
     635    f.close();
    768636
    769637    return true;
    770638}
     639
  • mythtv/mythtv/programs/mythfilldatabase/xmltvparser.h

    diff -Naur build/mythtv/mythtv/programs/mythfilldatabase/xmltvparser.h build3/mythtv/mythtv/programs/mythfilldatabase/xmltvparser.h
    old new  
    1717{
    1818  public:
    1919    XMLTVParser();
    20     void lateInit();
    2120
    2221    static ChannelInfo *parseChannel(QDomElement &element, QUrl &baseUrl);
    2322    ProgInfo *parseProgram(QDomElement &element);