All pastes #1236549 Raw Edit

Someone

public text v1 · immutable
#1236549 ·published 2008-10-26 01:29 UTC
rendered paste body
Index: mythnews/mythnews/newsengine.cpp
===================================================================
--- mythnews/mythnews/newsengine.cpp	(revision 18874)
+++ mythnews/mythnews/newsengine.cpp	(working copy)
@@ -8,8 +8,6 @@
 // QT headers
 #include <QFile>
 #include <QDataStream>
-#include <q3urloperator.h>
-#include <q3network.h>
 
 // Myth headers
 #include <mythtv/mythcontext.h>
@@ -41,38 +39,41 @@
 {
 }
 
-NewsSite::NewsSite(const QString &name,
-                   const QString &url,
+NewsArticle::NewsArticle() :
+    m_title(QString::null),
+    m_desc(QString::null),
+    m_articleURL(QString::null),
+    m_thumbnail(QString::null),
+    m_mediaURL(QString::null),
+    m_enclosure(QString::null)
+{
+}
+
+NewsSite::NewsSite(const QString   &name,
+                   const QString   &url,
                    const QDateTime &updated,
-                   const bool &podcast)
-    : QObject()
+                   const bool       podcast) :
+    QObject(), m_name(name),  m_url(url), m_urlReq(url),
+    m_desc(QString::null), m_updated(updated),
+    m_destDir(GetConfDir()+"/MythNews"),
+    /*m_data(),*/
+    m_state(NewsSite::Success),
+    m_errorString(QString::null),
+    m_imageURL(""),
+    m_podcast(podcast)
 {
-    m_url     = url;
-    m_name    = name;
-    m_updated = updated;
-    m_podcast = podcast;
-    m_state   = NewsSite::Success;
+}
 
-    m_destDir  = GetConfDir();
-    m_destDir += "/MythNews";
-
+void NewsSite::deleteLater()
+{
+    HttpPool::GetSingleton()->RemoveListener(this);
     m_articleList.clear();
-
-    m_data.resize(0);
-    m_urlOp = new Q3UrlOperator(m_url);
-
-    connect(m_urlOp, SIGNAL(data(const QByteArray&, Q3NetworkOperation*)),
-            this, SLOT(slotGotData(const QByteArray&, Q3NetworkOperation*)));
-    connect(m_urlOp, SIGNAL(finished(Q3NetworkOperation*)),
-            this, SLOT(slotFinished(Q3NetworkOperation*)));
+    QObject::deleteLater();
 }
 
 NewsSite::~NewsSite()
 {
-    m_urlOp->disconnect(this, 0, 0, 0);
-    m_urlOp->stop();
-    m_urlOp->deleteLater();
-    m_articleList.clear();
+    HttpPool::GetSingleton()->RemoveListener(this);
 }
 
 void NewsSite::insertNewsArticle(const NewsArticle &item)
@@ -131,16 +132,16 @@
 void NewsSite::retrieve(void)
 {
     stop();
-
     m_state = NewsSite::Retrieving;
     m_data.resize(0);
     m_articleList.clear();
-    m_urlOp->get(m_url);
+    m_urlReq = QUrl(m_url);
+    HttpPool::GetSingleton()->AddURLRequest(m_urlReq, this);
 }
 
 void NewsSite::stop(void)
 {
-    m_urlOp->stop();
+    HttpPool::GetSingleton()->RemoveURLRequest(m_urlReq, this);
 }
 
 bool NewsSite::successful(void) const
@@ -153,16 +154,34 @@
     return m_errorString;
 }
 
-void NewsSite::slotFinished(Q3NetworkOperation* op)
+void NewsSite::Updated(QHttp::Error      error,
+                       const QUrl       &url,
+                       const QByteArray &data)
 {
-    if (op->state() == Q3NetworkProtocol::StDone &&
-        op->errorCode() == Q3NetworkProtocol::NoError)
+    if (url != m_urlReq)
     {
+        return;
+    }
 
-        QFile xmlFile(m_destDir+QString("/")+m_name);
-        if (xmlFile.open( QIODevice::WriteOnly )) {
-            QDataStream stream( &xmlFile );
-            stream.writeRawData(m_data.constData(), m_data.size());
+    if (QHttp::NoError != error)
+    {
+        m_state = NewsSite::RetrieveFailed;
+        emit finished(this);
+        return;
+    }
+
+    m_data = data;
+
+    if (m_name.isEmpty())
+    {
+        m_state = NewsSite::WriteFailed;
+    }
+    else
+    {
+        QFile xmlFile(QString("%1/%2").arg(m_destDir).arg(m_name));
+        if (xmlFile.open(QIODevice::WriteOnly))
+        {
+            xmlFile.write(m_data);
             xmlFile.close();
             m_updated = QDateTime::currentDateTime();
             m_state = NewsSite::Success;
@@ -170,14 +189,12 @@
         else
         {
             m_state = NewsSite::WriteFailed;
-            VERBOSE(VB_IMPORTANT, "MythNews: NewsEngine: Write failed");
         }
     }
-    else
-    {
-        m_state = NewsSite::RetrieveFailed;
-    }
 
+    if (NewsSite::WriteFailed == m_state)
+        VERBOSE(VB_IMPORTANT, "MythNews: NewsEngine: Write failed");
+
     emit finished(this);
 }
 
@@ -402,20 +419,6 @@
     }
 }
 
-void NewsSite::slotGotData(const QByteArray &data,
-                           Q3NetworkOperation* op)
-{
-    if (op)
-    {
-        const char *charData = data.data();
-        int len = data.count();
-
-        int size = m_data.size();
-        m_data.resize(size + len);
-        memcpy(m_data.data()+size, charData, len);
-    }
-}
-
 void NewsSite::ReplaceHtmlChar(QString &s)
 {
     s.replace("&amp;", "&");
Index: mythnews/mythnews/newsengine.h
===================================================================
--- mythnews/mythnews/newsengine.h	(revision 18874)
+++ mythnews/mythnews/newsengine.h	(working copy)
@@ -15,6 +15,8 @@
 class Q3NetworkOperation;
 class NewsSite;
 
+#include "httppool.h"
+
 class NewsArticle
 {
   public:
@@ -25,6 +27,7 @@
                 const QString &thumbnail, const QString &mediaURL,
                 const QString &enclosure);
     NewsArticle(const QString &title);
+    NewsArticle();
 
     QString title(void)       const { return m_title;      }
     QString description(void) const { return m_desc;       }
@@ -45,7 +48,7 @@
 
 // -------------------------------------------------------
 
-class NewsSite : public QObject
+class NewsSite : public QObject, public HttpListener
 {
     Q_OBJECT
 
@@ -72,8 +75,9 @@
         }
     };
 
-    NewsSite(const QString &name, const QString &url,
-             const QDateTime &updated, const bool &podcast);
+    NewsSite(const QString   &name,    const QString &url,
+             const QDateTime &updated, const bool     podcast);
+    virtual void deleteLater();
 
     QString   url(void)  const;
     QString   name(void) const;
@@ -96,11 +100,16 @@
     bool     successful(void) const;
     QString  errorMsg(void) const;
 
+    virtual void Updated(QHttp::Error      error,
+                         const QUrl       &url,
+                         const QByteArray &data);
+
   private:
     ~NewsSite();
 
     QString    m_name;
     QString    m_url;
+    QUrl       m_urlReq;
     QString    m_desc;
     QDateTime  m_updated;
     QString    m_destDir;
@@ -111,19 +120,11 @@
     bool       m_podcast;
 
     NewsArticle::List m_articleList;
-    Q3UrlOperator     *m_urlOp;
-
     void ReplaceHtmlChar( QString &s);
 
   signals:
 
     void finished(NewsSite* item);
-
-  private slots:
-
-    void slotFinished(Q3NetworkOperation*);
-    void slotGotData(const QByteArray &data,
-                     Q3NetworkOperation* op);
 };
 
 class NewsSiteItem
Index: mythnews/mythnews/httppool.cpp
===================================================================
--- mythnews/mythnews/httppool.cpp	(revision 0)
+++ mythnews/mythnews/httppool.cpp	(revision 0)
@@ -0,0 +1,155 @@
+#include "httppool.h"
+#include "httphandler.h"
+
+// Myth headers
+#include <mythverbose.h>
+
+#define LOC      QString("HttpPool: ")
+#define LOC_WARN QString("HttpPool, Warning: ")
+#define LOC_ERR  QString("HttpPool, Error: ")
+
+HttpPool *HttpPool::singleton;
+
+HttpPool::HttpPool(uint max_connections) : m_maxConnections(max_connections)
+{
+}
+
+HttpPool::~HttpPool()
+{
+    while (!m_hostToHandler.empty())
+    {
+        HostToHandler::iterator it = m_hostToHandler.begin();
+        HttpHandler *h = *it;
+        m_hostToHandler.erase(it);
+        h->deleteLater();
+    }
+}
+
+void HttpPool::AddURLRequest(const QUrl &url, HttpListener *listener)
+{
+    QMutexLocker locker(&m_lock);
+
+    VERBOSE(VB_NETWORK, LOC + QString("AddUrlRequest(%1, 0x%2)")
+            .arg(url.toString()).arg((uint64_t)listener,0,16));
+
+    bool in_queue = m_urlToListener.find(url) != m_urlToListener.end();
+
+    m_urlToListener.insert(url, listener);
+    set<HttpListener*>::iterator lit = m_listeners.find(listener);
+    if (lit == m_listeners.end())
+        m_listeners.insert(listener);
+
+    if (in_queue)
+        return;
+
+    HostToHandler::iterator it = m_hostToHandler.find(url.host());
+
+    if (it != m_hostToHandler.end())
+    {
+        (*it)->AddURLRequest(url);
+    }
+    else if ((uint)m_hostToHandler.size() < m_maxConnections)
+    {
+        HttpHandler *hh = new HttpHandler(this);
+        m_hostToHandler[url.host()] = hh;
+        hh->AddURLRequest(url);
+    }
+    else
+    {
+        HostToHandler::iterator it = m_hostToHandler.begin();
+        for (; it != m_hostToHandler.end(); ++it)
+        {
+            if (!(*it)->HasPendingRequests())
+                break;
+        }
+        if (it != m_hostToHandler.end())
+        {
+            HttpHandler *hh = *it;
+            m_hostToHandler.erase(it);
+            m_hostToHandler[url.host()] = hh;
+            hh->AddURLRequest(url);
+        }
+        else
+        {
+            m_httpQueue.push_back(HttpRequest(url,listener));
+        }
+    }
+}
+
+void HttpPool::RemoveURLRequest(const QUrl &url, HttpListener *listener)
+{
+    QMutexLocker locker(&m_lock);
+
+    VERBOSE(VB_NETWORK, LOC + QString("RemoveUrlRequest(%1, 0x%2)")
+            .arg(url.toString()).arg((uint64_t)listener,0,16));
+}
+
+void HttpPool::RemoveListener(HttpListener *listener)
+{
+    QMutexLocker locker(&m_lock);
+
+    VERBOSE(VB_NETWORK, LOC + QString("RemoveListener(0x%1)")
+            .arg((uint64_t)listener,0,16));
+    set<HttpListener*>::iterator it = m_listeners.find(listener);
+    if (it != m_listeners.end())
+        m_listeners.erase(it);
+}
+
+void HttpPool::Update(QHttp::Error      error,
+                      const QUrl        &url,
+                      const QByteArray &data)
+{
+    QMutexLocker locker(&m_lock);
+
+    UrlToListener::iterator it = m_urlToListener.find(url);
+    for (; (it != m_urlToListener.end()) && (it.key() == url); ++it)
+    {
+        HttpListener *listener = *it;
+        if (m_listeners.find(listener) != m_listeners.end())
+            listener->Updated(error, url, data);
+    }
+    m_urlToListener.remove(url);
+}
+
+void HttpPool::Done(const QString &host, HttpHandler *handler)
+{
+    QMutexLocker locker(&m_lock);
+
+    VERBOSE(VB_NETWORK, LOC + QString("Done(%1, 0x%2)")
+            .arg(host).arg((uint64_t)handler,0,16));
+
+    HostToHandler::iterator it = m_hostToHandler.find(host);
+    if (handler != *it)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Done(%1, 0x%2)")
+                .arg(host).arg((uint64_t)handler,0,16));
+        return;
+    }
+
+    while (!m_httpQueue.empty())
+    {
+        QUrl          url      = m_httpQueue.front().m_url;
+        HttpListener *listener = m_httpQueue.front().m_listener;
+        m_httpQueue.pop_front();
+
+        if (m_listeners.find(listener) == m_listeners.end())
+            continue;
+
+        if (url.host() != host)
+        {
+            m_hostToHandler.erase(it);
+            m_hostToHandler[url.host()] = handler;
+        }
+        handler->AddURLRequest(url);
+        break;
+    }
+}
+
+HttpPool *HttpPool::GetSingleton()
+{
+    static QMutex lock;
+    QMutexLocker locker(&lock);
+    if (!singleton)
+        singleton = new HttpPool();
+    return singleton;
+}
Index: mythnews/mythnews/httphandler.cpp
===================================================================
--- mythnews/mythnews/httphandler.cpp	(revision 0)
+++ mythnews/mythnews/httphandler.cpp	(revision 0)
@@ -0,0 +1,151 @@
+#include "httphandler.h"
+#include "httppool.h"
+
+// Qt headers
+#include <QHttp>
+
+// Myth headers
+#include <mythverbose.h>
+
+#define LOC      QString("HttpHandler: ")
+#define LOC_WARN QString("HttpHandler, Warning: ")
+#define LOC_ERR  QString("HttpHandler, Error: ")
+
+//////////////////////////////////////////////////////////////////////
+
+HttpHandler::HttpHandler(HttpPool *pool) :
+    m_pool(pool), m_qhttp(new QHttp())
+{
+    connect(m_qhttp, SIGNAL(done(bool)),
+            this,    SLOT(Done(bool)));
+    connect(m_qhttp, SIGNAL(requestFinished(int,bool)),
+            this,    SLOT(RequestFinished(int,bool)));
+    connect(m_qhttp, SIGNAL(requestStarted(int)),
+            this,    SLOT(RequestStarted(int)));
+    connect(m_qhttp, SIGNAL(stateChanged(int)),
+            this,    SLOT(StateChanged(int)));
+    connect(m_qhttp, SIGNAL(responseHeaderReceived(const QHttpResponseHeader&)),
+            this,    SLOT(ResponseHeaderReceived(const QHttpResponseHeader&)));
+}
+
+void HttpHandler::TeardownAll(void)
+{
+    QMutexLocker locker(&m_lock);
+
+    if (m_qhttp)
+    {
+        m_qhttp->abort();
+        m_qhttp->disconnect();
+        m_qhttp->deleteLater();
+    }
+    m_pool = NULL;
+    m_qhttp = NULL;
+}
+
+bool HttpHandler::HasPendingRequests(void) const
+{
+    QMutexLocker locker(&m_lock);
+    return m_qhttp->hasPendingRequests();
+}
+
+void HttpHandler::AddURLRequest(const QUrl &url)
+{
+    QMutexLocker locker(&m_lock);
+
+    if (!m_qhttp->hasPendingRequests())
+        Get(url);
+    else
+        m_urls.push_back(url);
+}
+
+void HttpHandler::Get(const QUrl &url)
+{
+    m_cur_url       = url;
+    m_cur_status_id = 0;
+
+    m_qhttp->setHost(m_cur_url.host());
+
+    QString path = (m_cur_url.path().isEmpty()) ? "/" : m_cur_url.path();
+    m_cur_get_id = m_qhttp->get(path);
+}
+
+void HttpHandler::RemoveURLRequest(const QUrl &url)
+{
+    QMutexLocker locker(&m_lock);
+
+    URLQueue urls = m_urls;
+    m_urls.clear();
+    while (!urls.empty())
+    {
+        QUrl item = urls.front();
+        urls.pop_front();
+        if (item != url)
+            m_urls.push_back(item);
+    }
+
+    if (url == m_cur_url)
+    {
+        m_cur_url       = QUrl();
+        m_cur_status_id = 0;
+        m_cur_get_id    = 0;
+        m_qhttp->abort();
+    }
+}
+
+void HttpHandler::Done(bool error)
+{
+    QMutexLocker locker(&m_lock);
+
+    VERBOSE(VB_NETWORK, LOC + QString("Done(%1) url: %2")
+            .arg(error).arg(m_cur_url.toString()));
+
+    if (m_pool)
+        m_pool->Done(m_cur_url.host(), this);
+}
+
+void HttpHandler::ResponseHeaderReceived(const QHttpResponseHeader &resp)
+{
+    QMutexLocker locker(&m_lock);
+
+    VERBOSE(VB_NETWORK, LOC + QString("ResponseHeaderReceived(%1,%2) url: %3")
+            .arg(resp.statusCode()).arg(resp.reasonPhrase())
+            .arg(m_cur_url.toString()));
+    m_cur_status_id = resp.statusCode();
+}
+
+void HttpHandler::RequestFinished(int id, bool error)
+{
+    QMutexLocker locker(&m_lock);
+
+    VERBOSE(VB_NETWORK, LOC + QString("RequestFinished(%1,%2) url: %3")
+            .arg(id).arg(error).arg(m_cur_url.toString()));
+    if (error && m_pool)
+        m_pool->Update(m_qhttp->error(), m_cur_url, QByteArray());
+    else if ((id == m_cur_get_id) && m_pool)
+        m_pool->Update(QHttp::NoError, m_cur_url, m_qhttp->readAll());
+    else
+        return;
+
+    if (!m_urls.empty())
+    {
+        Get(m_urls.front());
+        m_urls.pop_front();
+    }
+}
+
+void HttpHandler::RequestStarted(int id)
+{
+    QMutexLocker locker(&m_lock);
+
+    VERBOSE(VB_NETWORK, LOC + QString("RequestStarted(%1) url: %2")
+            .arg(id).arg(m_cur_url.toString()));
+}
+
+void HttpHandler::StateChanged(int state)
+{
+    QMutexLocker locker(&m_lock);
+
+    VERBOSE(VB_NETWORK, LOC + QString("StateChanged(%1) url: %2")
+            .arg(state).arg(m_cur_url.toString()));
+}
+
Index: mythnews/mythnews/httppool.h
===================================================================
--- mythnews/mythnews/httppool.h	(revision 0)
+++ mythnews/mythnews/httppool.h	(revision 0)
@@ -0,0 +1,65 @@
+#ifndef _HTTP_POOL_H_
+#define _HTTP_POOL_H_
+
+// C++ headers
+#include <set>
+#include <deque>
+using namespace std;
+
+// Qt headers
+#include <QString>
+#include <QMutex>
+#include <QHttp>
+#include <QUrl>
+
+class HttpListener
+{
+  public:
+    virtual void Updated(QHttp::Error      error,
+                         const QUrl       &url,
+                         const QByteArray &data) = 0;
+};
+
+class HttpRequest
+{
+  public:
+    HttpRequest(const QUrl &url, HttpListener *listener) :
+        m_url(url), m_listener(listener) {}
+
+    QUrl          m_url;
+    HttpListener *m_listener;
+};
+
+class HttpHandler;
+typedef QMap<QString,HttpHandler*> HostToHandler;
+typedef QMultiMap<QUrl,HttpListener*> UrlToListener;
+
+class HttpPool
+{
+  public:
+    HttpPool(uint max_connections = 20);
+    ~HttpPool();
+
+    void AddURLRequest(const QUrl &url, HttpListener *listener);
+    void RemoveURLRequest(const QUrl &url, HttpListener *listener);
+    void RemoveListener(HttpListener *listener);
+
+    static HttpPool *GetSingleton();
+
+    void Update(QHttp::Error      error,
+                const QUrl        &url,
+                const QByteArray &data);
+    void Done(const QString &host, HttpHandler *handler);
+
+  private:
+    mutable QMutex      m_lock;
+    uint                m_maxConnections;
+    deque<HttpRequest>  m_httpQueue;
+    set<HttpListener*>  m_listeners;
+    UrlToListener       m_urlToListener;
+    HostToHandler       m_hostToHandler;
+
+    static HttpPool *singleton;
+};
+
+#endif // _HTTP_POOL_H_
Index: mythnews/mythnews/mythnews.cpp
===================================================================
--- mythnews/mythnews/mythnews.cpp	(revision 18874)
+++ mythnews/mythnews/mythnews.cpp	(working copy)
@@ -179,35 +179,33 @@
         return;
 
     NewsSite *site = NULL;
-    NewsArticle *article = NULL;
+    NewsArticle article;
 
     if (GetFocusWidget() == m_articlesList)
     {
-        article = qVariantValue<NewsArticle*>(selected->GetData());
+        article = m_articles[selected];
         if (m_sitesList->GetItemCurrent())
             site = qVariantValue<NewsSite*>
-                                (m_sitesList->GetItemCurrent()->GetData());
+                (m_sitesList->GetItemCurrent()->GetData());
     }
     else
     {
         site = qVariantValue<NewsSite*>(selected->GetData());
         if (m_articlesList->GetItemCurrent())
-            article = qVariantValue<NewsArticle*>
-                                (m_articlesList->GetItemCurrent()->GetData());
+            article = m_articles[m_articlesList->GetItemCurrent()];
     }
 
     if (GetFocusWidget() == m_articlesList)
     {
-
-        if (article)
+        if (!article.title().isEmpty())
         {
 
             if (m_titleText)
-                m_titleText->SetText(article->title());
+                m_titleText->SetText(article.title());
 
             if (m_descText)
             {
-                QString artText = article->description();
+                QString artText = article.description();
                 // Replace paragraph and break HTML with newlines
                 if( artText.indexOf(QRegExp("</(p|P)>")) )
                 {
@@ -237,7 +235,7 @@
                 m_descText->SetText(artText);
             }
 
-            if (!article->thumbnail().isEmpty())
+            if (!article.thumbnail().isEmpty())
             {
                 QString fileprefix = GetConfDir();
 
@@ -251,7 +249,7 @@
                 if (!dir.exists())
                     dir.mkdir(fileprefix);
 
-                QString url = article->thumbnail();
+                QString url = article.thumbnail();
                 QString sFilename = QString("%1/%2")
                     .arg(fileprefix)
                     .arg(qChecksum(url.toLocal8Bit().constData(),
@@ -309,7 +307,7 @@
                 }
             }
 
-            if (!article->enclosure().isEmpty())
+            if (!article.enclosure().isEmpty())
             {
                 if (!m_downloadImage->IsVisible())
                     m_downloadImage->Show();
@@ -317,7 +315,7 @@
             else
                 m_downloadImage->Hide();
 
-            if (!article->enclosure().isEmpty())
+            if (!article.enclosure().isEmpty())
             {
                 if (!m_enclosureImage->IsVisible())
                     m_enclosureImage->Show();
@@ -547,13 +545,14 @@
         return;
 
     m_articlesList->Reset();
+    m_articles.clear();
 
     NewsArticle::List::iterator it = site->articleList().begin();
     for (; it != site->articleList().end(); ++it)
     {
         MythUIButtonListItem *item =
             new MythUIButtonListItem(m_articlesList, (*it).title());
-        item->SetData(qVariantFromValue(&(*it)));
+        m_articles[item] = *it;
     }
 }
 
@@ -567,13 +566,14 @@
         return;
 
     m_articlesList->Reset();
+    m_articles.clear();
 
     NewsArticle::List::iterator it = site->articleList().begin();
     for (; it != site->articleList().end(); ++it)
     {
         MythUIButtonListItem *item =
             new MythUIButtonListItem(m_articlesList, (*it).title());
-        item->SetData(qVariantFromValue(&(*it)));
+        m_articles[item] = *it;
     }
 
     updateInfoView(item);
@@ -689,82 +689,84 @@
 
 void MythNews::slotViewArticle(MythUIButtonListItem *articlesListItem)
 {
-    if (articlesListItem && !articlesListItem->GetData().isNull())
-    {
-        NewsArticle *article = qVariantValue<NewsArticle*>
-                                                (articlesListItem->GetData());
-        if(article)
-        {
-            if (!article->enclosure().isEmpty())
-            {
-                QString cmdURL(article->enclosure());
-                // Handle special cases for media here
-                // YouTube: Fetch the mediaURL page and parse out the video URL
-                if (cmdURL.contains("youtube.com"))
-                {
-                    cmdURL = QString(article->mediaURL());
-                    QString mediaPage = HttpComms::getHttp(cmdURL);
-                    if (!mediaPage.isEmpty())
-                    {
-                        // If this breaks in the future, we are building the URL
-                        // to download a video.  At this time, this requires
-                        // the video_id and the t argument
-                        // from the source HTML of the page
-                        int playerPos = mediaPage.indexOf("swfArgs") + 7;
+    QMap<MythUIButtonListItem*,NewsArticle>::const_iterator it =
+        m_articles.find(articlesListItem);
 
-                        int tArgStart = mediaPage.indexOf("\"t\": \"",
-                                                       playerPos) + 6;
-                        int tArgEnd = mediaPage.indexOf("\"", tArgStart);
-                        QString tArgString = mediaPage.mid(tArgStart,
-                                                           tArgEnd - tArgStart);
+    if (it == m_articles.end())
+        return;
 
-                        int vidStart = mediaPage.indexOf("\"video_id\": \"",
-                                                      playerPos) + 13;
-                        int vidEnd = mediaPage.indexOf("\"", vidStart);
-                        QString vidString = mediaPage.mid(vidStart,
-                                                          vidEnd - vidStart);
+    if ((*it).title().isEmpty())
+        return;
 
-                        cmdURL = QString("http://youtube.com/get_video.php"
-                                         "?video_id=%2&t=%1")
-                                 .arg(tArgString).arg(vidString);
-                        VERBOSE(VB_GENERAL,
-                                QString("MythNews: VideoURL %1").arg(cmdURL));
-                    }
-                }
+    const NewsArticle article = *it;
 
-                QString fileprefix = GetConfDir();
+    if (article.enclosure().isEmpty())
+    {
+        QString cmdUrl(article.articleURL());
+        cmdUrl.replace('\'', "%27");
 
-                QDir dir(fileprefix);
-                if (!dir.exists())
-                     dir.mkdir(fileprefix);
+        QString cmd = QString("%1 %2 '%3'")
+            .arg(browser)
+            .arg(zoom)
+            .arg(cmdUrl);
+        gContext->GetMainWindow()->AllowInput(false);
+        myth_system(cmd, MYTH_SYSTEM_DONT_BLOCK_PARENT);
+        gContext->GetMainWindow()->AllowInput(true);
+        return;
+    }
 
-                fileprefix += "/MythNews";
+    QString cmdURL(article.enclosure());
 
-                dir = QDir(fileprefix);
-                if (!dir.exists())
-                    dir.mkdir(fileprefix);
+    // Handle special cases for media here
+    // YouTube: Fetch the mediaURL page and parse out the video URL
+    if (cmdURL.contains("youtube.com"))
+    {
+        cmdURL = QString(article.mediaURL());
+        QString mediaPage = HttpComms::getHttp(cmdURL);
+        if (!mediaPage.isEmpty())
+        {
+            // If this breaks in the future, we are building the URL
+            // to download a video.  At this time, this requires
+            // the video_id and the t argument
+            // from the source HTML of the page
+            int playerPos = mediaPage.indexOf("swfArgs") + 7;
 
-                QString sFilename(fileprefix + "/newstempfile");
+            int tArgStart = mediaPage.indexOf("\"t\": \"",
+                                              playerPos) + 6;
+            int tArgEnd = mediaPage.indexOf("\"", tArgStart);
+            QString tArgString = mediaPage.mid(tArgStart,
+                                               tArgEnd - tArgStart);
 
-                if (getHttpFile(sFilename, cmdURL))
-                {
-                    playVideo(sFilename);
-                }
-            }
-            else {
-                QString cmdUrl(article->articleURL());
-                cmdUrl.replace('\'', "%27");
+            int vidStart = mediaPage.indexOf("\"video_id\": \"",
+                                             playerPos) + 13;
+            int vidEnd = mediaPage.indexOf("\"", vidStart);
+            QString vidString = mediaPage.mid(vidStart,
+                                              vidEnd - vidStart);
 
-                QString cmd = QString("%1 %2 '%3'")
-                     .arg(browser)
-                     .arg(zoom)
-                     .arg(cmdUrl);
-                gContext->GetMainWindow()->AllowInput(false);
-                myth_system(cmd, MYTH_SYSTEM_DONT_BLOCK_PARENT);
-                gContext->GetMainWindow()->AllowInput(true);
-            }
+            cmdURL = QString("http://youtube.com/get_video.php"
+                             "?video_id=%2&t=%1")
+                .arg(tArgString).arg(vidString);
+            VERBOSE(VB_GENERAL,
+                    QString("MythNews: VideoURL %1").arg(cmdURL));
         }
     }
+
+    QString fileprefix = GetConfDir();
+
+    QDir dir(fileprefix);
+    if (!dir.exists())
+        dir.mkdir(fileprefix);
+
+    fileprefix += "/MythNews";
+
+    dir = QDir(fileprefix);
+    if (!dir.exists())
+        dir.mkdir(fileprefix);
+
+    QString sFilename(fileprefix + "/newstempfile");
+
+    if (getHttpFile(sFilename, cmdURL))
+        playVideo(sFilename);
 }
 
 void MythNews::ShowEditDialog(bool edit)
Index: mythnews/mythnews/httphandler.h
===================================================================
--- mythnews/mythnews/httphandler.h	(revision 0)
+++ mythnews/mythnews/httphandler.h	(revision 0)
@@ -0,0 +1,52 @@
+#ifndef _HTTP_HANDLER_H_
+#define _HTTP_HANDLER_H_
+
+// C++ headers
+#include <deque>
+using namespace std;
+
+// Qt headers
+#include <QMutex>
+#include <QUrl>
+
+typedef deque<QUrl> URLQueue;
+
+class QHttp;
+class QHttpResponseHeader;
+class HttpPool;
+
+class HttpHandler : public QObject
+{
+    Q_OBJECT
+  public:
+    HttpHandler(HttpPool *pool);
+    virtual void deleteLater(void) { TeardownAll(); QObject::deleteLater(); }
+
+    bool HasPendingRequests(void) const;
+    void AddURLRequest(const QUrl &url);
+    void RemoveURLRequest(const QUrl &url);
+
+  protected slots:
+    void Done(bool error);
+    void ResponseHeaderReceived(const QHttpResponseHeader &resp);
+    void RequestFinished(int id, bool error);
+    void RequestStarted(int id);
+    void StateChanged(int state);
+
+  private:
+    ~HttpHandler() { TeardownAll(); }
+    void TeardownAll();
+    void Get(const QUrl &url);
+
+  private:
+    mutable QMutex m_lock;
+    URLQueue       m_urls;
+    QUrl           m_cur_url;
+    uint           m_cur_status_id;
+    int            m_cur_get_id;
+    HttpPool      *m_pool;
+    QHttp         *m_qhttp;
+
+};
+
+#endif // _HTTP_HANDLER_H_
Index: mythnews/mythnews/mythnews.h
===================================================================
--- mythnews/mythnews/mythnews.h	(revision 18874)
+++ mythnews/mythnews/mythnews.h	(working copy)
@@ -69,6 +69,7 @@
 
     MythUIButtonList *m_sitesList;
     MythUIButtonList *m_articlesList;
+    QMap<MythUIButtonListItem*,NewsArticle> m_articles;
 
     MythUIText *m_updatedText;
     MythUIText *m_titleText;
Index: mythnews/mythnews/mythnews.pro
===================================================================
--- mythnews/mythnews/mythnews.pro	(revision 18874)
+++ mythnews/mythnews/mythnews.pro	(working copy)
@@ -1,6 +1,8 @@
 include ( ../../mythconfig.mak )
 include ( ../../settings.pro )
 include ( ../../programs-libs.pro )
+
+QT += network sql xml
  
 TEMPLATE = lib
 CONFIG += plugin thread warn_on debug
@@ -21,7 +23,7 @@
 SOURCES += main.cpp mythnews.cpp mythnewsconfig.cpp mythnewseditor.cpp
 SOURCES += newsengine.cpp newsdbutil.cpp dbcheck.cpp
 
-#The following line was inserted by qt3to4
-QT += network sql xml qt3support
+HEADERS += httppool.h   httphandler.h
+SOURCES += httppool.cpp httphandler.cpp
 
 include ( ../../libs-targetfix.pro )