00001
00002 #include <QFile>
00003
00004
00005 #include <mythlogging.h>
00006 #include <mythdirs.h>
00007
00008
00009 #include "newssite.h"
00010
00011 #define LOC QString("NewsSite: ")
00012 #define LOC_WARN QString("NewsSite, Warning: ")
00013 #define LOC_ERR QString("NewsSite, Error: ")
00014
00015 NewsSite::NewsSite(const QString &name,
00016 const QString &url,
00017 const QDateTime &updated,
00018 const bool podcast) :
00019 QObject(),
00020 m_lock(QMutex::Recursive),
00021 m_name(name), m_url(url), m_urlReq(url),
00022 m_desc(QString::null), m_updated(updated),
00023 m_destDir(GetConfDir()+"/MythNews"),
00024
00025 m_state(NewsSite::Success),
00026 m_errorString(QString::null),
00027 m_updateErrorString(QString::null),
00028 m_imageURL(""),
00029 m_podcast(podcast)
00030 {
00031 }
00032
00033 void NewsSite::deleteLater()
00034 {
00035 QMutexLocker locker(&m_lock);
00036 MythHttpPool::GetSingleton()->RemoveListener(this);
00037 m_articleList.clear();
00038 QObject::deleteLater();
00039 }
00040
00041 NewsSite::~NewsSite()
00042 {
00043 QMutexLocker locker(&m_lock);
00044 MythHttpPool::GetSingleton()->RemoveListener(this);
00045 }
00046
00047 void NewsSite::insertNewsArticle(const NewsArticle &item)
00048 {
00049 QMutexLocker locker(&m_lock);
00050 m_articleList.push_back(item);
00051 }
00052
00053 void NewsSite::clearNewsArticles(void)
00054 {
00055 QMutexLocker locker(&m_lock);
00056 m_articleList.clear();
00057 }
00058
00059 void NewsSite::retrieve(void)
00060 {
00061 QMutexLocker locker(&m_lock);
00062
00063 stop();
00064 m_state = NewsSite::Retrieving;
00065 m_data.resize(0);
00066 m_errorString = QString::null;
00067 m_updateErrorString = QString::null;
00068 m_articleList.clear();
00069 m_urlReq = QUrl(m_url);
00070 MythHttpPool::GetSingleton()->AddUrlRequest(m_urlReq, this);
00071 }
00072
00073 void NewsSite::stop(void)
00074 {
00075 QMutexLocker locker(&m_lock);
00076 MythHttpPool::GetSingleton()->RemoveUrlRequest(m_urlReq, this);
00077 }
00078
00079 bool NewsSite::successful(void) const
00080 {
00081 QMutexLocker locker(&m_lock);
00082 return (m_state == NewsSite::Success);
00083 }
00084
00085 QString NewsSite::errorMsg(void) const
00086 {
00087 QMutexLocker locker(&m_lock);
00088 return m_errorString;
00089 }
00090
00091 QString NewsSite::url(void) const
00092 {
00093 QMutexLocker locker(&m_lock);
00094 return m_url;
00095 }
00096
00097 QString NewsSite::name(void) const
00098 {
00099 QMutexLocker locker(&m_lock);
00100 return m_name;
00101 }
00102
00103 bool NewsSite::podcast(void) const
00104 {
00105 QMutexLocker locker(&m_lock);
00106 return m_podcast;
00107 }
00108
00109 QString NewsSite::description(void) const
00110 {
00111 QMutexLocker locker(&m_lock);
00112 QString result;
00113
00114 if (!m_desc.isEmpty())
00115 result = m_desc;
00116
00117 if (!m_errorString.isEmpty())
00118 result += m_desc.isEmpty() ? m_errorString : '\n' + m_errorString;
00119
00120 return result;
00121 }
00122
00123 QString NewsSite::imageURL(void) const
00124 {
00125 QMutexLocker locker(&m_lock);
00126 return m_imageURL;
00127 }
00128
00129 NewsArticle::List NewsSite::GetArticleList(void) const
00130 {
00131 QMutexLocker locker(&m_lock);
00132 return m_articleList;
00133 }
00134
00135 QDateTime NewsSite::lastUpdated(void) const
00136 {
00137 QMutexLocker locker(&m_lock);
00138 return m_updated;
00139 }
00140
00141 unsigned int NewsSite::timeSinceLastUpdate(void) const
00142 {
00143 QMutexLocker locker(&m_lock);
00144
00145 QDateTime curTime(QDateTime::currentDateTime());
00146 unsigned int min = m_updated.secsTo(curTime)/60;
00147 return min;
00148 }
00149
00150 void NewsSite::Update(QHttp::Error error,
00151 const QString &error_str,
00152 const QUrl &url,
00153 uint http_status_id,
00154 const QString &http_status_str,
00155 const QByteArray &data)
00156 {
00157 QMutexLocker locker(&m_lock);
00158
00159 if (url != m_urlReq)
00160 {
00161 return;
00162 }
00163
00164 if (QHttp::NoError != error)
00165 {
00166 LOG(VB_GENERAL, LOG_ERR, LOC + "HTTP Connection Error" +
00167 QString("\n\t\t\tExplanation: %1: %2")
00168 .arg(error).arg(error_str));
00169
00170 m_state = NewsSite::RetrieveFailed;
00171 m_updateErrorString = QString("%1: %2").arg(error).arg(error_str);
00172 emit finished(this);
00173 return;
00174 }
00175
00176 if (200 != http_status_id)
00177 {
00178 LOG(VB_GENERAL, LOG_ERR, LOC + "HTTP Protocol Error" +
00179 QString("\n\t\t\tExplanation: %1: %2")
00180 .arg(http_status_id).arg(http_status_str));
00181
00182 m_state = NewsSite::RetrieveFailed;
00183 m_updateErrorString =
00184 QString("%1: %2").arg(http_status_id).arg(http_status_str);
00185 emit finished(this);
00186 return;
00187 }
00188
00189 m_updateErrorString = QString::null;
00190 m_data = data;
00191
00192 if (m_name.isEmpty())
00193 {
00194 m_state = NewsSite::WriteFailed;
00195 }
00196 else
00197 {
00198 QFile xmlFile(QString("%1/%2").arg(m_destDir).arg(m_name));
00199 if (xmlFile.open(QIODevice::WriteOnly))
00200 {
00201 xmlFile.write(m_data);
00202 xmlFile.close();
00203 m_updated = QDateTime::currentDateTime();
00204 m_state = NewsSite::Success;
00205 }
00206 else
00207 {
00208 m_state = NewsSite::WriteFailed;
00209 }
00210 }
00211
00212 if (NewsSite::WriteFailed == m_state)
00213 LOG(VB_GENERAL, LOG_ERR, LOC + "Write failed");
00214
00215 emit finished(this);
00216 }
00217
00218 void NewsSite::process(void)
00219 {
00220 QMutexLocker locker(&m_lock);
00221
00222 m_articleList.clear();
00223
00224 m_errorString = "";
00225 if (RetrieveFailed == m_state)
00226 m_errorString = tr("Retrieve Failed. ")+"\n";
00227
00228 QDomDocument domDoc;
00229
00230 QFile xmlFile(m_destDir+QString("/")+m_name);
00231 if (!xmlFile.exists())
00232 {
00233 insertNewsArticle(NewsArticle(tr("Failed to retrieve news")));
00234 m_errorString += tr("No Cached News.");
00235 if (!m_updateErrorString.isEmpty())
00236 m_errorString += "\n" + m_updateErrorString;
00237 return;
00238 }
00239
00240 if (!xmlFile.open(QIODevice::ReadOnly))
00241 {
00242 insertNewsArticle(NewsArticle(tr("Failed to retrieve news")));
00243 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to open xmlfile");
00244 if (!m_updateErrorString.isEmpty())
00245 m_errorString += "\n" + m_updateErrorString;
00246 return;
00247 }
00248
00249 if (!domDoc.setContent(&xmlFile))
00250 {
00251 insertNewsArticle(NewsArticle(tr("Failed to retrieve news")));
00252 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to set content from xmlfile");
00253 m_errorString += tr("Failed to read downloaded file.");
00254 if (!m_updateErrorString.isEmpty())
00255 m_errorString += "\n" + m_updateErrorString;
00256 return;
00257 }
00258
00259 if (RetrieveFailed == m_state)
00260 {
00261 m_errorString += tr("Showing Cached News.");
00262 if (!m_updateErrorString.isEmpty())
00263 m_errorString += "\n" + m_updateErrorString;
00264 }
00265
00266
00267 QString rootName = domDoc.documentElement().nodeName();
00268 if (rootName == "rss" || rootName == "rdf:RDF")
00269 {
00270 parseRSS(domDoc);
00271 xmlFile.close();
00272 return;
00273 }
00274 else if (rootName == "feed")
00275 {
00276 parseAtom(domDoc);
00277 xmlFile.close();
00278 return;
00279 }
00280 else {
00281 LOG(VB_GENERAL, LOG_ERR, LOC + "XML-file is not valid RSS-feed");
00282 m_errorString += tr("XML-file is not valid RSS-feed");
00283 return;
00284 }
00285
00286 }
00287
00288 void NewsSite::parseRSS(QDomDocument domDoc)
00289 {
00290 QMutexLocker locker(&m_lock);
00291
00292 QDomNode channelNode = domDoc.documentElement().namedItem("channel");
00293
00294 m_desc = channelNode.namedItem("description")
00295 .toElement().text().simplified();
00296
00297 QDomNode imageNode = channelNode.namedItem("image");
00298 if (!imageNode.isNull())
00299 m_imageURL = imageNode.namedItem("url").toElement().text().simplified();
00300
00301 QDomNodeList items = domDoc.elementsByTagName("item");
00302
00303 for (unsigned int i = 0; i < (unsigned) items.count(); i++)
00304 {
00305 QDomNode itemNode = items.item(i);
00306 QString title = ReplaceHtmlChar(itemNode.namedItem("title")
00307 .toElement().text().simplified());
00308
00309 QDomNode descNode = itemNode.namedItem("description");
00310 QString description = QString::null;
00311 if (!descNode.isNull())
00312 {
00313 description = descNode.toElement().text().simplified();
00314 description = ReplaceHtmlChar(description);
00315 }
00316
00317 QDomNode linkNode = itemNode.namedItem("link");
00318 QString url = QString::null;
00319 if (!linkNode.isNull())
00320 url = linkNode.toElement().text().simplified();
00321
00322 QDomNode enclosureNode = itemNode.namedItem("enclosure");
00323 QString enclosure = QString::null;
00324 QString enclosure_type = QString::null;
00325 QString thumbnail = QString::null;
00326 if (!enclosureNode.isNull())
00327 {
00328 QDomAttr enclosureURL = enclosureNode.toElement()
00329 .attributeNode("url");
00330
00331 if (!enclosureURL.isNull())
00332 enclosure = enclosureURL.value();
00333
00334 QDomAttr enclosureType = enclosureNode.toElement()
00335 .attributeNode("type");
00336 if (!enclosureType.isNull())
00337 {
00338 enclosure_type = enclosureType.value();
00339
00340 if (enclosure_type == "image/jpeg")
00341 {
00342 thumbnail = enclosure;
00343 enclosure = QString::null;
00344 }
00345 }
00346 }
00347
00349
00350
00352
00353
00354
00355
00356 QDomNode mediaGroup = itemNode.namedItem("media:group");
00357 if (!mediaGroup.isNull())
00358 itemNode = mediaGroup;
00359
00360 QDomNode thumbNode = itemNode.namedItem("media:thumbnail");
00361 if (!thumbNode.isNull())
00362 {
00363 QDomAttr thumburl = thumbNode.toElement().attributeNode("url");
00364 if (!thumburl.isNull())
00365 thumbnail = thumburl.value();
00366 }
00367
00368 QDomNode playerNode = itemNode.namedItem("media:player");
00369 QString mediaurl = QString::null;
00370 if (!playerNode.isNull())
00371 {
00372 QDomAttr mediaURL = playerNode.toElement().attributeNode("url");
00373 if (!mediaURL.isNull())
00374 mediaurl = mediaURL.value();
00375 }
00376
00377
00378 descNode = itemNode.namedItem("media:description");
00379 if (!descNode.isNull())
00380 description = descNode.toElement().text().simplified();
00381
00382 if (enclosure.isEmpty())
00383 {
00384 QDomNode contentNode = itemNode.namedItem("media:content");
00385 if (!contentNode.isNull())
00386 {
00387 QDomAttr enclosureURL = contentNode.toElement()
00388 .attributeNode("url");
00389
00390 if (!enclosureURL.isNull())
00391 enclosure = enclosureURL.value();
00392
00393 QDomAttr enclosureType = contentNode.toElement()
00394 .attributeNode("type");
00395
00396 if (!enclosureType.isNull())
00397 enclosure_type = enclosureType.value();
00398 }
00399 }
00400
00401 (void) enclosure_type;
00402
00403 insertNewsArticle(NewsArticle(title, description, url,
00404 thumbnail, mediaurl, enclosure));
00405 }
00406 }
00407
00408 void NewsSite::parseAtom(QDomDocument domDoc)
00409 {
00410 QDomNodeList entries = domDoc.elementsByTagName("entry");
00411
00412 for (unsigned int i = 0; i < (unsigned) entries.count(); i++)
00413 {
00414 QDomNode itemNode = entries.item(i);
00415 QString title = ReplaceHtmlChar(itemNode.namedItem("title").toElement()
00416 .text().simplified());
00417
00418 QDomNode summNode = itemNode.namedItem("summary");
00419 QString description = QString::null;
00420 if (!summNode.isNull())
00421 {
00422 description = ReplaceHtmlChar(
00423 summNode.toElement().text().simplified());
00424 }
00425
00426 QDomNode linkNode = itemNode.namedItem("link");
00427 QString url = QString::null;
00428 if (!linkNode.isNull())
00429 {
00430 QDomAttr linkHref = linkNode.toElement().attributeNode("href");
00431 if (!linkHref.isNull())
00432 url = linkHref.value();
00433 }
00434
00435 insertNewsArticle(NewsArticle(title, description, url));
00436 }
00437 }
00438
00439 QString NewsSite::ReplaceHtmlChar(const QString &orig)
00440 {
00441 if (orig.isEmpty())
00442 return orig;
00443
00444 QString s = orig;
00445 s.replace("&", "&");
00446 s.replace("<", "<");
00447 s.replace(">", ">");
00448 s.replace(""", "\"");
00449 s.replace("'", "\'");
00450 s.replace("…",QChar(8230));
00451 s.replace("é",QChar(233));
00452 s.replace("—", QChar(8212));
00453 s.replace(" ", " ");
00454 s.replace(" ", QChar(160));
00455 s.replace("á", QChar(225));
00456 s.replace("‘", QChar(8216));
00457 s.replace("’", QChar(8217));
00458 s.replace("'", "\'");
00459 s.replace("–", QChar(8211));
00460
00461 s.replace("ä", QChar(0x00e4));
00462 s.replace("ö", QChar(0x00f6));
00463 s.replace("ü", QChar(0x00fc));
00464 s.replace("Ä", QChar(0x00c4));
00465 s.replace("Ö", QChar(0x00d6));
00466 s.replace("Ü", QChar(0x00dc));
00467 s.replace("ß", QChar(0x00df));
00468
00469 return s;
00470 }