MythTV  master
mhegic.cpp
Go to the documentation of this file.
1 /* MHEG Interaction Channel
2  * Copyright 2011 Lawrence Rust <lvr at softsystem dot co dot uk>
3  */
4 #include "mhegic.h"
5 
6 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
7 // C/C++ lib
8 #include <cstdlib>
9 using std::getenv;
10 #endif
11 
12 // Qt
13 #include <QByteArray>
14 #include <QMutexLocker>
15 #include <QNetworkRequest>
16 #include <QStringList>
17 #include <QScopedPointer>
18 #include <QApplication>
19 
20 // Myth
21 #include "netstream.h"
22 #include "mythlogging.h"
23 #include "mythcorecontext.h"
24 
25 #define LOC QString("[mhegic] ")
26 
27 
28 MHInteractionChannel::MHInteractionChannel(QObject* parent) : QObject(parent)
29 {
30  setObjectName("MHInteractionChannel");
31  moveToThread(&NAMThread::manager());
32 }
33 
34 // virtual
36 {
37  QMutexLocker locker(&m_mutex);
38  for (const auto & req : qAsConst(m_pending))
39  req->deleteLater();
40  for (const auto & req : qAsConst(m_finished))
41  req->deleteLater();
42 }
43 
44 // Get network status
45 // static
47 {
49  {
50  LOG(VB_MHEG, LOG_INFO, LOC + "WARN network is unavailable");
51  return kInactive;
52  }
53 
54  if (!gCoreContext->GetBoolSetting("EnableMHEG", false))
55  return kDisabled;
56 
57 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
58  QStringList opts = QString(getenv("MYTHMHEG")).split(':');
59 #else
60  QStringList opts = qEnvironmentVariable("MYTHMHEG").split(':');
61 #endif
62  if (opts.contains("noice", Qt::CaseInsensitive))
63  return kDisabled;
64  if (opts.contains("ice", Qt::CaseInsensitive))
65  return kActive;
66  return gCoreContext->GetBoolSetting("EnableMHEGic", true) ? kActive : kDisabled;
67 }
68 
69 static inline bool isCached(const QUrl& url)
70 {
71  return NetStream::GetLastModified(url).isValid();
72 }
73 
74 // Is a file ready to read?
75 bool MHInteractionChannel::CheckFile(const QString& csPath, const QByteArray &cert)
76 {
77  QUrl url(csPath);
78  QMutexLocker locker(&m_mutex);
79 
80  // Is it complete?
81  if (m_finished.contains(url))
82  return true;
83 
84  // Is it pending?
85  if (m_pending.contains(url))
86  return false; // It's pending so unavailable
87 
88  // Is it in the cache?
89  if (isCached(url))
90  return true; // It's cached
91 
92  // Queue a request
93  LOG(VB_MHEG, LOG_DEBUG, LOC + QString("CheckFile queue %1").arg(csPath));
94  QScopedPointer< NetStream > p(new NetStream(url, NetStream::kPreferCache, cert));
95  if (!p || !p->IsOpen())
96  {
97  LOG(VB_MHEG, LOG_WARNING, LOC + QString("CheckFile failed %1").arg(csPath) );
98  return false;
99  }
100 
101  connect(p.data(), &NetStream::Finished, this, &MHInteractionChannel::slotFinished );
102  m_pending.insert(url, p.take());
103 
104  return false; // It's now pending so unavailable
105 }
106 
107 // Read a file. -1= error, 0= OK, 1= not ready
109 MHInteractionChannel::GetFile(const QString &csPath, QByteArray &data,
110  const QByteArray &cert /*=QByteArray()*/)
111 {
112  QUrl url(csPath);
113  QMutexLocker locker(&m_mutex);
114 
115  // Is it pending?
116  if (m_pending.contains(url))
117  return kPending;
118 
119  // Is it complete?
120  QScopedPointer< NetStream > p(m_finished.take(url));
121  if (p)
122  {
123  if (p->GetError() == QNetworkReply::NoError)
124  {
125  data = p->ReadAll();
126  LOG(VB_MHEG, LOG_DEBUG, LOC + QString("GetFile finished %1").arg(csPath) );
127  return kSuccess;
128  }
129 
130  LOG(VB_MHEG, LOG_WARNING, LOC + QString("GetFile failed %1").arg(csPath) );
131  return kError;
132  }
133 
134  // Is it in the cache?
135  if (isCached(url))
136  {
137  LOG(VB_MHEG, LOG_DEBUG, LOC + QString("GetFile cache read %1").arg(csPath) );
138 
139  locker.unlock();
140 
142  if (req.WaitTillFinished(3s) && req.GetError() == QNetworkReply::NoError)
143  {
144  data = req.ReadAll();
145  LOG(VB_MHEG, LOG_DEBUG, LOC + QString("GetFile cache read %1 bytes %2")
146  .arg(data.size()).arg(csPath) );
147  return kSuccess;
148  }
149 
150  LOG(VB_MHEG, LOG_WARNING, LOC + QString("GetFile cache read failed %1").arg(csPath) );
151  //return kError;
152  // Retry
153  locker.relock();
154  }
155 
156  // Queue a download
157  LOG(VB_MHEG, LOG_DEBUG, LOC + QString("GetFile queue %1").arg(csPath) );
158  p.reset(new NetStream(url, NetStream::kPreferCache, cert));
159  if (!p || !p->IsOpen())
160  {
161  LOG(VB_MHEG, LOG_WARNING, LOC + QString("GetFile failed %1").arg(csPath) );
162  return kError;
163  }
164 
165  connect(p.data(), &NetStream::Finished, this, &MHInteractionChannel::slotFinished );
166  m_pending.insert(url, p.take());
167 
168  return kPending;
169 }
170 
171 // signal from NetStream
173 {
174  auto* p = qobject_cast< NetStream* >(obj);
175  if (!p)
176  return;
177 
178  QUrl url = p->Url();
179 
180  if (p->GetError() == QNetworkReply::NoError)
181  {
182  LOG(VB_MHEG, LOG_DEBUG, LOC + QString("Finished %1").arg(url.toString()) );
183  }
184  else
185  {
186  LOG(VB_MHEG, LOG_WARNING, LOC + QString("Finished %1").arg(p->GetErrorString()) );
187  }
188 
189  p->disconnect();
190 
191  QMutexLocker locker(&m_mutex);
192 
193  if (m_pending.remove(url) < 1)
194  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Finished %1 wasn't pending").arg(url.toString()) );
195  m_finished.insert(url, p);
196 }
197 
198 /* End of file */
LOC
#define LOC
Definition: mhegic.cpp:25
NetStream::ReadAll
QByteArray ReadAll()
Definition: netstream.cpp:684
MHInteractionChannel::slotFinished
void slotFinished(QObject *obj)
Definition: mhegic.cpp:172
isCached
static bool isCached(const QUrl &url)
Definition: mhegic.cpp:69
MHInteractionChannel::status
static EStatus status()
Definition: mhegic.cpp:46
MHInteractionChannel::~MHInteractionChannel
~MHInteractionChannel() override
Definition: mhegic.cpp:35
MHInteractionChannel::kError
@ kError
Definition: mhegic.h:35
NetStream::GetLastModified
static QDateTime GetLastModified(const QUrl &url)
Definition: netstream.cpp:728
MHInteractionChannel::GetFile
EResult GetFile(const QString &csPath, QByteArray &data, const QByteArray &cert=QByteArray())
Definition: mhegic.cpp:109
MHInteractionChannel::EResult
EResult
Definition: mhegic.h:35
arg
arg(title).arg(filename).arg(doDelete))
MHInteractionChannel::m_mutex
QMutex m_mutex
Definition: mhegic.h:46
MHInteractionChannel::m_finished
map_t m_finished
Definition: mhegic.h:49
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
NetStream::WaitTillFinished
bool WaitTillFinished(std::chrono::milliseconds timeout)
Definition: netstream.cpp:649
MHInteractionChannel::kSuccess
@ kSuccess
Definition: mhegic.h:35
MHInteractionChannel::kPending
@ kPending
Definition: mhegic.h:35
netstream.h
mythlogging.h
MHInteractionChannel::EStatus
EStatus
Definition: mhegic.h:27
hardwareprofile.config.p
p
Definition: config.py:33
MHInteractionChannel::m_pending
map_t m_pending
Definition: mhegic.h:48
NetStream::Finished
void Finished(QObject *)
MHInteractionChannel::kDisabled
@ kDisabled
Definition: mhegic.h:27
NetStream
Stream content from a URI.
Definition: netstream.h:30
MHInteractionChannel::CheckFile
bool CheckFile(const QString &csPath, const QByteArray &cert=QByteArray())
Definition: mhegic.cpp:75
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
NetStream::GetError
QNetworkReply::NetworkError GetError() const
Definition: netstream.cpp:666
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:926
NetStream::isAvailable
static bool isAvailable()
Public helpers.
Definition: netstream.cpp:721
mhegic.h
mythcorecontext.h
MHInteractionChannel::MHInteractionChannel
MHInteractionChannel(QObject *parent=nullptr)
Definition: mhegic.cpp:28
NetStream::kPreferCache
@ kPreferCache
Definition: netstream.h:35
MHInteractionChannel::kActive
@ kActive
Definition: mhegic.h:27
NAMThread::manager
static NAMThread & manager()
NetworkAccessManager event loop thread.
Definition: netstream.cpp:738
MHInteractionChannel::kInactive
@ kInactive
Definition: mhegic.h:27
NetStream::kAlwaysCache
@ kAlwaysCache
Definition: netstream.h:35