MythTV  master
netsearch.cpp
Go to the documentation of this file.
1 // qt
2 #include <QString>
3 #include <QMetaType>
4 #include <QDateTime>
5 #include <QFileInfo>
6 #include <QImageReader>
7 
8 // myth
9 #include <mythdb.h>
10 #include <mythcontext.h>
11 #include <mythdbcon.h>
12 #include <mythdirs.h>
13 #include <mythsystemlegacy.h>
14 #include <mythprogressdialog.h>
15 #include <storagegroup.h>
16 #include <remotefile.h>
17 #include <netgrabbermanager.h>
18 #include <netutils.h>
20 #include <metadata/videoutils.h>
21 #include <rssparse.h>
22 #include <mythcoreutil.h>
23 #include <mythuitext.h>
24 #include <mythuiimage.h>
25 #include <mythsorthelper.h>
26 
27 #include "netsearch.h"
28 #include "netcommon.h"
29 #include "rsseditor.h"
30 #include "searcheditor.h"
31 
32 using namespace std;
33 
34 // ---------------------------------------------------
35 
37  : NetBase(parent, name),
38  m_mythXML(GetMythXMLURL())
39 {
40 }
41 
43 {
44  // Load the theme for this screen
45  bool foundtheme = LoadWindowFromXML("netvision-ui.xml", "netsearch", this);
46 
47  if (!foundtheme)
48  return false;
49 
50  m_siteList = dynamic_cast<MythUIButtonList *> (GetChild("sites"));
51  m_searchResultList = dynamic_cast<MythUIButtonList *> (GetChild("results"));
52 
53  m_pageText = dynamic_cast<MythUIText *> (GetChild("page"));
54  m_noSites = dynamic_cast<MythUIText *> (GetChild("nosites"));
55 
56  m_thumbImage = dynamic_cast<MythUIImage *> (GetChild("preview"));
57 
58  m_downloadable = dynamic_cast<MythUIStateType *> (GetChild("downloadable"));
59 
60  m_progress = dynamic_cast<MythUIProgressBar *> (GetChild("progress"));
61 
62  if (m_progress)
63  m_progress->SetVisible(false);
64 
65  if (m_noSites)
66  m_noSites->SetVisible(false);
67 
68  m_search = dynamic_cast<MythUITextEdit *> (GetChild("search"));
69 
71  {
72  LOG(VB_GENERAL, LOG_ERR, "Theme is missing critical theme elements.");
73  return false;
74  }
75 
76  m_search->SetMaxLength(255);
77 
78  // UI Hookups
79  connect(m_siteList, SIGNAL(itemSelected(MythUIButtonListItem *)),
80  SLOT(SlotItemChanged()));
81  connect(m_siteList, SIGNAL(itemClicked(MythUIButtonListItem *)),
82  SLOT(DoSearch(void)));
83  connect(m_searchResultList, SIGNAL(itemClicked(MythUIButtonListItem *)),
84  SLOT(StreamWebVideo(void)));
85  connect(m_searchResultList, SIGNAL(itemSelected(MythUIButtonListItem *)),
86  SLOT(SlotItemChanged()));
87 
89 
91 
92  return true;
93 }
94 
96 {
98 }
99 
101 {
102  if (m_netSearch)
103  {
104  m_netSearch->disconnect();
105  m_netSearch->deleteLater();
106  m_netSearch = nullptr;
107  }
108 }
109 
111 {
113 
114  if (m_grabberList.count() == 0 && m_noSites)
115  m_noSites->SetVisible(true);
116  else if (m_noSites)
117  m_noSites->SetVisible(false);
118 
119  if (m_grabberList.isEmpty())
120  RunSearchEditor();
121 }
122 
123 bool NetSearch::keyPressEvent(QKeyEvent *event)
124 {
125  if (GetFocusWidget()->keyPressEvent(event))
126  return true;
127 
128  QStringList actions;
129  bool handled = GetMythMainWindow()->TranslateKeyPress("Internet Video",
130  event, actions);
131 
132  for (int i = 0; i < actions.size() && !handled; i++)
133  {
134  QString action = actions[i];
135  handled = true;
136 
137  if (action == "MENU")
138  ShowMenu();
139  else if (action == "PAGELEFT" && m_pagenum > 1)
140  {
141  if (m_prevPageToken.isEmpty())
142  SkipPagesBack();
143  else
144  GetLastResults();
145  }
146  else if (action == "PAGERIGHT" && m_searchResultList->GetCount() > 0 &&
147  m_pagenum + 10 < m_maxpage)
148  {
149  if (m_nextPageToken.isEmpty())
151  else
152  GetMoreResults();
153  }
154  else if (action == "PREVVIEW" && m_pagenum > 1)
155  GetLastResults();
156  else if (action == "NEXTVIEW" && m_searchResultList->GetCount() > 0 &&
158  GetMoreResults();
159  else
160  handled = false;
161  }
162 
163  if (!handled && MythScreenType::keyPressEvent(event))
164  handled = true;
165 
166  return handled;
167 }
168 
170 {
171  QString label = tr("Search Options");
172 
173  MythDialogBox *menuPopup = new MythDialogBox(label, m_popupStack,
174  "mythnetvisionmenupopup");
175 
176  if (menuPopup->Create())
177  {
178  m_popupStack->AddScreen(menuPopup);
179 
180  menuPopup->SetReturnEvent(this, "options");
181 
182  if (m_searchResultList->GetCount() > 0)
183  {
184  ResultItem *item = GetStreamItem();
185  if (item)
186  {
187  if (item->GetDownloadable())
188  menuPopup->AddButton(tr("Stream Video"),
189  SLOT(StreamWebVideo()));
190  menuPopup->AddButton(tr("Open Web Link"), SLOT(ShowWebVideo()));
191 
192  QString filename = GetDownloadFilename(item->GetTitle(),
193  item->GetMediaURL());
194  bool exists = false;
195 
196  if (filename.startsWith("myth://"))
197  exists = RemoteFile::Exists(filename);
198  else
199  exists = QFile::exists(filename);
200 
201  if (item->GetDownloadable() &&
203  {
204  if (exists)
205  menuPopup->AddButton(tr("Play"),
206  SLOT(DoPlayVideo(filename)));
207  else
208  menuPopup->AddButton(tr("Save This Video"),
209  SLOT(DoDownloadAndPlay()));
210  }
211 
212  if (item->GetDownloadable() &&
214  exists)
215  {
216  menuPopup->AddButton(tr("Delete"), SLOT(SlotDeleteVideo()));
217  }
218  }
219  }
220 
221  if (m_pagenum > 1)
222  menuPopup->AddButton(tr("Previous Page"), SLOT(GetLastResults()));
224  menuPopup->AddButton(tr("Next Page"), SLOT(GetMoreResults()));
225  if (m_pagenum > 1 && m_prevPageToken.isEmpty())
226  menuPopup->AddButton(tr("Skip 10 Pages Back"),
227  SLOT(SkipPagesBack()));
229  m_nextPageToken.isEmpty())
230  menuPopup->AddButton(tr("Skip 10 Pages Forward"),
231  SLOT(SkipPagesForward()));
232 
233  menuPopup->AddButton(tr("Manage Search Scripts"),
234  SLOT(RunSearchEditor()));
235  }
236  else
237  delete menuPopup;
238 }
239 
241 {
242  m_siteList->Reset();
243 
244  for (GrabberScript::scriptList::iterator i = m_grabberList.begin();
245  i != m_grabberList.end(); ++i)
246  {
247  MythUIButtonListItem *item =
248  new MythUIButtonListItem(m_siteList, (*i)->GetTitle());
249  item->SetText((*i)->GetTitle(), "title");
250  item->SetData((*i)->GetCommandline());
251  QString thumb = QString("%1mythnetvision/icons/%2").arg(GetShareDir())
252  .arg((*i)->GetImage());
253  item->SetImage(thumb);
254  }
255 }
256 
258 {
260 
261  int numScripts = m_siteList->GetCount();
262  for (int i = 0; i < numScripts; i++)
263  m_siteList->GetItemAt(i)->SetText("", "count");
264 
265  if (m_pageText)
266  m_pageText->Reset();
267 
268  m_pagenum = 1;
269  m_maxpage = 1;
270 
271  QString cmd = m_siteList->GetDataValue().toString();
272  QString grabber = m_siteList->GetItemCurrent()->GetText();
273  QString query = m_search->GetText();
274 
275  if (query.isEmpty())
276  return;
277 
278  QFileInfo fi(cmd);
279  m_currentCmd = fi.fileName();
281  m_currentSearch = query;
282 
283  QString title = tr("Searching %1 for \"%2\"...")
284  .arg(grabber).arg(query);
285  OpenBusyPopup(title);
286 
287  if (!m_netSearch)
288  {
289  m_netSearch = new QNetworkAccessManager(this);
290  connect(m_netSearch, SIGNAL(finished(QNetworkReply*)),
291  SLOT(SearchFinished(void)));
292  }
293 
295  QUrl req(init.toEncoded(), QUrl::TolerantMode);
296  LOG(VB_GENERAL, LOG_INFO,
297  QString("Using Search URL %1").arg(req.toString()));
298  m_reply = m_netSearch->get(QNetworkRequest(req));
299 }
300 
302 {
304 
305  m_pagenum--;
306 
307  QString title = tr("Changing to page %1 of search \"%2\"...")
308  .arg(QString::number(m_pagenum))
309  .arg(m_currentSearch);
310  OpenBusyPopup(title);
311 
312  QString page = m_prevPageToken.isEmpty() ? QString::number(m_pagenum) :
314 
315  QUrl req =
317  m_reply = m_netSearch->get(QNetworkRequest(req));
318 }
319 
321 {
323 
324  m_pagenum++;
325 
326  QString title = tr("Changing to page %1 of search \"%2\"...")
327  .arg(QString::number(m_pagenum))
328  .arg(m_currentSearch);
329  OpenBusyPopup(title);
330 
331  QString page = m_nextPageToken.isEmpty() ? QString::number(m_pagenum) :
333 
334  QUrl req =
336  m_reply = m_netSearch->get(QNetworkRequest(req));
337 }
338 
340 {
341  if (m_pagenum < 11)
342  m_pagenum = 2;
343  else
344  m_pagenum = m_pagenum - 10 + 1;
345 
346  GetLastResults();
347 }
348 
350 {
351  m_pagenum = m_pagenum + 10 - 1;
352 
353  GetMoreResults();
354 }
355 
357 {
358  CloseBusyPopup();
359 
360  Search *item = new Search();
361  QByteArray data = m_reply->readAll();
362  item->SetData(data);
363 
364  item->process();
365 
366  uint searchresults = item->numResults();
367  uint returned = item->numReturned();
368  uint firstitem = item->numIndex();
369 
370  m_nextPageToken = item->nextPageToken();
371  m_prevPageToken = item->prevPageToken();
372 
373  if (returned > 0)
375  SetText(QString::number(searchresults), "count");
376  else
377  return;
378 
379  if (firstitem + returned == searchresults)
381  else
382  {
383  m_maxpage = searchresults / returned; // Whole pages
384  if (searchresults % returned != 0) // Partial page?
385  m_maxpage++;
386  }
387  if (m_pageText && m_maxpage > 0 && m_pagenum > 0 && returned > 0)
388  m_pageText->SetText(QString("%1 / %2")
389  .arg(QString::number(m_pagenum))
390  .arg(QString::number(m_maxpage)));
391 
392  ResultItem::resultList list = item->GetVideoList();
393  PopulateResultList(list);
395 }
396 
398 {
399  CloseBusyPopup();
400 
401  QString message = tr("Timed out waiting for query to "
402  "finish. API might be down.");
403 
404  if (!m_okPopup)
405  {
406  m_okPopup = new MythConfirmationDialog(m_popupStack, message, false);
407 
408  if (m_okPopup->Create())
410  else
411  {
412  delete m_okPopup;
413  m_okPopup = nullptr;
414  }
415  }
416 }
417 
419 {
420  for (ResultItem::resultList::iterator i = list.begin();
421  i != list.end(); ++i)
422  {
423  QString title = (*i)->GetTitle();
424  MythUIButtonListItem *item =
426  qVariantFromValue(*i));
427  InfoMap metadataMap;
428  (*i)->toMap(metadataMap);
429  item->SetTextFromMap(metadataMap);
430 
431  if (!(*i)->GetThumbnail().isEmpty())
432  {
433  QString dlfile = (*i)->GetThumbnail();
434 
435  if (dlfile.contains("%SHAREDIR%"))
436  {
437  dlfile.replace("%SHAREDIR%", GetShareDir());
438  item->SetImage(dlfile);
439  }
440  else
441  {
442  uint pos = m_searchResultList->GetItemPos(item);
443 
444  m_imageDownload->addThumb((*i)->GetTitle(),
445  (*i)->GetThumbnail(),
446  qVariantFromValue<uint>(pos));
447  }
448  }
449  }
450 }
451 
453 {
454  return m_searchResultList->GetDataValue().value<ResultItem*>();
455 }
456 
458 {
460 
461  SearchEditor *searchedit = new SearchEditor(mainStack, "mythnetsearchedit");
462 
463  if (searchedit->Create())
464  {
465  connect(searchedit, SIGNAL(ItemsChanged()),
466  this, SLOT(DoListRefresh()));
467 
468  mainStack->AddScreen(searchedit);
469  }
470  else
471  delete searchedit;
472 }
473 
475 {
477  if (m_grabberList.isEmpty())
478  RunSearchEditor();
479 
480  LoadData();
481 }
482 
484 {
486 
487  if (item && GetFocusWidget() == m_searchResultList)
488  {
490 
491  if (m_downloadable)
492  {
493  if (item->GetDownloadable())
495  else
497  }
498  }
499  else if (GetFocusWidget() == m_siteList)
500  {
502  std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
503  QString title = btn->GetText();
504 
505  ResultItem res(title, sh->doTitle(title), // title, sortTitle
506  QString(), QString(), // subtitle, sortSubtitle
507  QString(), // description
508  QString(), QString(), QString(), QString(),
509  QDateTime(), nullptr, nullptr, -1, QString(), QStringList(),
510  QString(), QStringList(), 0, 0, QString(),
511  false, QStringList(), 0, 0, false);
512 
513  SetTextAndThumbnail(btn, &res);
514  }
515 }
516 
518 {
519  InfoMap metadataMap;
520  item->toMap(metadataMap);
521  SetTextFromMap(metadataMap);
522 
523  SetThumbnail(btn);
524 }
525 
527 {
528  if (m_thumbImage)
529  {
530  QString filename = btn->GetImageFilename();
531  if (filename.contains("%SHAREDIR%"))
532  filename.replace("%SHAREDIR%", GetShareDir());
533 
534  if (!filename.isEmpty())
535  {
536  m_thumbImage->SetFilename(filename);
537  m_thumbImage->Load();
538  }
539  else
540  m_thumbImage->Reset();
541  }
542 }
543 
544 void NetSearch::customEvent(QEvent *event)
545 {
546  if (event->type() == ThumbnailDLEvent::kEventType)
547  {
548  ThumbnailDLEvent *tde = (ThumbnailDLEvent *)event;
549  ThumbnailData *data = tde->m_thumb;
550 
551  if (!data)
552  return;
553 
554  QString title = data->title;
555  QString file = data->url;
556  uint pos = data->data.value<uint>();
557 
558  if (file.isEmpty() || !((uint)m_searchResultList->GetCount() >= pos))
559  return;
560 
562 
563  if (!item)
564  return;
565 
566  if (item->GetText() == title)
567  item->SetImage(file);
568 
569  if (m_searchResultList->GetItemCurrent() == item)
570  SetThumbnail(item);
571  }
572  else
573  NetBase::customEvent(event);
574 }
MythConfirmationDialog * m_okPopup
Definition: netsearch.h:47
void StreamWebVideo(void)
Definition: netbase.cpp:87
uint m_maxpage
Definition: netsearch.h:56
MythUIText * m_noSites
Definition: netsearch.h:44
void SetVisible(bool visible) override
void DoDownloadAndPlay(void)
Definition: netbase.cpp:279
void DoSearch(void)
Definition: netsearch.cpp:257
Dialog asking for user confirmation.
void SetTextAndThumbnail(MythUIButtonListItem *btn, ResultItem *item)
Definition: netsearch.cpp:517
void SetData(QByteArray data)
void SearchTimeout(Search *item)
Definition: netsearch.cpp:397
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
void SetData(QVariant data)
uint numIndex()
int m_currentGrabber
Definition: netsearch.h:53
Basic menu dialog, message and a list of options.
QString GetImageFilename(const QString &name="") const
QNetworkReply * m_reply
Definition: netsearch.h:50
void ShowMenu(void) override
Definition: netsearch.cpp:169
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:136
int GetItemPos(MythUIButtonListItem *item) const
MythUIText * m_pageText
Definition: netsearch.h:43
unsigned int uint
Definition: compat.h:140
MythScreenStack * GetMainStack()
MythUIButtonListItem * GetItemAt(int pos) const
void ShowWebVideo(void)
Definition: netbase.cpp:106
uint numReturned()
MythUITextEdit * m_search
Definition: netsearch.h:41
void SearchFinished(void)
Definition: netsearch.cpp:356
void DoPlayVideo(const QString &filename)
Definition: netbase.cpp:306
uint m_pagenum
Definition: netsearch.h:55
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
void BuildFocusList(void)
void PopulateResultList(ResultItem::resultList list)
Definition: netsearch.cpp:418
uint numResults()
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
QString m_nextPageToken
Definition: netsearch.h:61
void DoListRefresh()
Definition: netsearch.cpp:474
void GetMoreResults()
Definition: netsearch.cpp:320
GrabberScript::scriptList m_grabberList
Definition: netbase.h:52
void AddButton(const QString &title, QVariant data=0, bool newMenu=false, bool setCurrent=false)
virtual void SetVisible(bool visible)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
void Load() override
Load data which will ultimately be displayed on-screen or used to determine what appears on-screen (S...
Definition: netsearch.cpp:95
const QString & GetMediaURL() const
Definition: rssparse.h:141
void SetMaxLength(const int length)
QString prevPageToken()
QString GetDownloadFilename(const QString &title, const QString &url)
Definition: netutils.cpp:851
void SlotDeleteVideo(void)
Definition: netbase.cpp:183
MythScreenStack * m_popupStack
Definition: netbase.h:47
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
const bool & GetDownloadable() const
Definition: rssparse.h:154
ResultItem::resultList GetVideoList()
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:84
QString GetShareDir(void)
Definition: mythdirs.cpp:222
MythUIProgressBar * m_progress
Definition: netsearch.h:46
void OpenBusyPopup(const QString &message="")
void Reset(void) override
Reset the image back to the default defined in the theme.
virtual void SetTextFromMap(const InfoMap &infoMap)
QVariant GetDataValue() const
void SetText(const QString &text, const QString &name="", const QString &state="")
MythUIImage * m_thumbImage
Definition: netbase.h:45
bool Create(void) override
void process(void)
const char * name
Definition: ParseText.cpp:328
GrabberScript::scriptList findAllDBSearchGrabbers(ArticleType type)
Definition: netutils.cpp:184
void customEvent(QEvent *levent) override
Definition: netsearch.cpp:544
QString GetMythXMLURL(void)
Definition: netcommon.cpp:32
void LoadData(void) override
Definition: netsearch.cpp:110
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
MythUIType * GetFocusWidget(void) const
QString m_mythXML
Definition: netsearch.h:57
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
MythMainWindow * GetMythMainWindow(void)
QString m_currentSearch
Definition: netsearch.h:52
QString GetText(const QString &name="") const
ThumbnailData * m_thumb
void SkipPagesBack()
Definition: netsearch.cpp:339
QUrl GetMythXMLSearch(const QString &url, const QString &query, const QString &grabber, const QString &pagenum)
Definition: netcommon.cpp:41
MythUIButtonList * m_siteList
Definition: netsearch.h:40
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:461
bool keyPressEvent(QKeyEvent *) override
Key event handler.
void SetThumbnail(MythUIButtonListItem *btn)
Definition: netsearch.cpp:526
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void CloseBusyPopup(void)
bool Create(void) override
Definition: netsearch.cpp:42
void customEvent(QEvent *event) override
Definition: netbase.cpp:222
QString nextPageToken()
bool Create(void) override
void SlotItemChanged(void)
Definition: netsearch.cpp:483
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
const QString & GetTitle() const
Definition: rssparse.h:134
void addThumb(QString title, QString url, QVariant data)
void SetReturnEvent(QObject *retobject, const QString &resultid)
MetadataImageDownload * m_imageDownload
Definition: netbase.h:49
MythUIStateType * m_downloadable
Definition: netbase.h:46
static void init(VideoFrame *vf, VideoFrameType _codec, unsigned char *_buf, int _width, int _height, int _size, const int *p=nullptr, const int *o=nullptr, float _aspect=-1.0F, double _rate=-1.0F, int _aligned=64)
Definition: mythframe.h:115
QString m_prevPageToken
Definition: netsearch.h:62
QString m_currentCmd
Definition: netsearch.h:54
bool DisplayState(const QString &name)
void GetLastResults()
Definition: netsearch.cpp:301
NetSearch(MythScreenStack *parent, const char *name=nullptr)
Definition: netsearch.cpp:36
MythUIButtonList * m_searchResultList
Definition: netsearch.h:39
bool SetFocusWidget(MythUIType *widget=nullptr)
bool keyPressEvent(QKeyEvent *) override
Key event handler.
Definition: netsearch.cpp:123
int GetCurrentPos() const
QNetworkAccessManager * m_netSearch
Definition: netsearch.h:49
void SkipPagesForward()
Definition: netsearch.cpp:349
QList< ResultItem * > resultList
Definition: rssparse.h:114
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:132
void RunSearchEditor()
Definition: netsearch.cpp:457
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
void FillGrabberButtonList(void)
Definition: netsearch.cpp:240
QString GetText(void) const
MythUIButtonListItem * GetItemCurrent() const
void LoadInBackground(QString message="")
bool Create(void) override
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
void toMap(InfoMap &metadataMap)
Definition: rssparse.cpp:76
ResultItem * GetStreamItem() override
Definition: netsearch.cpp:452