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