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 {
136 ShowMenu();
137 }
138 else if (action == "PAGELEFT" && m_pagenum > 1)
139 {
140 if (m_prevPageToken.isEmpty())
142 else
144 }
145 else if (action == "PAGERIGHT" && m_searchResultList->GetCount() > 0 &&
146 m_pagenum + 10 < m_maxpage)
147 {
148 if (m_nextPageToken.isEmpty())
150 else
152 }
153 else if (action == "PREVVIEW" && m_pagenum > 1)
154 {
156 }
157 else if (action == "NEXTVIEW" && m_searchResultList->GetCount() > 0 &&
159 {
161 }
162 else
163 {
164 handled = false;
165 }
166 }
167
168 if (!handled && MythScreenType::keyPressEvent(event))
169 handled = true;
170
171 return handled;
172}
173
175{
176 QString label = tr("Search Options");
177
178 auto *menuPopup = new MythDialogBox(label, m_popupStack,
179 "mythnetvisionmenupopup");
180
181 if (menuPopup->Create())
182 {
183 m_popupStack->AddScreen(menuPopup);
184
185 menuPopup->SetReturnEvent(this, "options");
186
187 if (m_searchResultList->GetCount() > 0)
188 {
189 ResultItem *item = GetStreamItem();
190 if (item)
191 {
192 if (item->GetDownloadable())
193 menuPopup->AddButton(tr("Stream Video"),
195 menuPopup->AddButton(tr("Open Web Link"), &NetSearch::ShowWebVideo);
196
197 QString filename = GetDownloadFilename(item->GetTitle(),
198 item->GetMediaURL());
199 bool exists = false;
200
201 if (filename.startsWith("myth://"))
203 else
205
206 if (item->GetDownloadable() &&
208 {
209 if (exists)
210 {
211 menuPopup->AddButton(tr("Play"),
212 qOverload<>(&NetSearch::DoPlayVideo));
213 }
214 else
215 {
216 menuPopup->AddButton(tr("Save This Video"),
218 }
219 }
220
221 if (item->GetDownloadable() &&
223 exists)
224 {
225 menuPopup->AddButton(tr("Delete"), &NetSearch::SlotDeleteVideo);
226 }
227 }
228 }
229
230 if (m_pagenum > 1)
231 menuPopup->AddButton(tr("Previous Page"), &NetSearch::GetLastResults);
233 menuPopup->AddButton(tr("Next Page"), &NetSearch::GetMoreResults);
234 if (m_pagenum > 1 && m_prevPageToken.isEmpty())
235 menuPopup->AddButton(tr("Skip 10 Pages Back"),
238 m_nextPageToken.isEmpty())
239 menuPopup->AddButton(tr("Skip 10 Pages Forward"),
241
242 menuPopup->AddButton(tr("Manage Search Scripts"),
244 }
245 else
246 {
247 delete menuPopup;
248 }
249}
250
252{
253 m_siteList->Reset();
254
255 for (const auto & g : std::as_const(m_grabberList))
256 {
257 auto *item = new MythUIButtonListItem(m_siteList, g->GetTitle());
258 item->SetText(g->GetTitle(), "title");
259 item->SetData(g->GetCommandline());
260 QString thumb = QString("%1mythnetvision/icons/%2")
261 .arg(GetShareDir(), g->GetImage());
262 item->SetImage(thumb);
263 }
264}
265
267{
269
270 int numScripts = m_siteList->GetCount();
271 for (int i = 0; i < numScripts; i++)
272 m_siteList->GetItemAt(i)->SetText("", "count");
273
274 if (m_pageText)
275 m_pageText->Reset();
276
277 m_pagenum = 1;
278 m_maxpage = 1;
279
280 QString cmd = m_siteList->GetDataValue().toString();
281 QString grabber = m_siteList->GetItemCurrent()->GetText();
282 QString query = m_search->GetText();
283
284 if (query.isEmpty())
285 return;
286
287 QFileInfo fi(cmd);
288 m_currentCmd = fi.fileName();
290 m_currentSearch = query;
291
292 QString title = tr("Searching %1 for \"%2\"...")
293 .arg(grabber, query);
294 OpenBusyPopup(title);
295
296 if (!m_netSearch)
297 {
298 m_netSearch = new QNetworkAccessManager(this);
299 connect(m_netSearch, &QNetworkAccessManager::finished,
301 }
302
304 QUrl req(init.toEncoded(), QUrl::TolerantMode);
305 LOG(VB_GENERAL, LOG_INFO,
306 QString("Using Search URL %1").arg(req.toString()));
307 m_reply = m_netSearch->get(QNetworkRequest(req));
308}
309
311{
313
314 m_pagenum--;
315
316 QString title = tr("Changing to page %1 of search \"%2\"...")
317 .arg(QString::number(m_pagenum), m_currentSearch);
318 OpenBusyPopup(title);
319
320 QString page = m_prevPageToken.isEmpty() ? QString::number(m_pagenum) :
322
323 QUrl req =
325 m_reply = m_netSearch->get(QNetworkRequest(req));
326}
327
329{
331
332 m_pagenum++;
333
334 QString title = tr("Changing to page %1 of search \"%2\"...")
335 .arg(QString::number(m_pagenum), m_currentSearch);
336 OpenBusyPopup(title);
337
338 QString page = m_nextPageToken.isEmpty() ? QString::number(m_pagenum) :
340
341 QUrl req =
343 m_reply = m_netSearch->get(QNetworkRequest(req));
344}
345
347{
348 if (m_pagenum < 11)
349 m_pagenum = 2;
350 else
351 m_pagenum = m_pagenum - 10 + 1;
352
354}
355
357{
358 m_pagenum = m_pagenum + 10 - 1;
359
361}
362
364{
366
367 auto *item = new Search();
368 QByteArray data = m_reply->readAll();
369 item->SetData(data);
370
371 item->process();
372
373 uint searchresults = item->numResults();
374 uint returned = item->numReturned();
375 uint firstitem = item->numIndex();
376
377 m_nextPageToken = item->nextPageToken();
378 m_prevPageToken = item->prevPageToken();
379
380 if (returned == 0)
381 return;
382
384 SetText(QString::number(searchresults), "count");
385
386 if (firstitem + returned == searchresults)
387 {
389 }
390 else
391 {
392 m_maxpage = searchresults / returned; // Whole pages
393 if (searchresults % returned != 0) // Partial page?
394 m_maxpage++;
395 }
396 if (m_pageText && m_maxpage > 0 && m_pagenum > 0)
397 {
398 m_pageText->SetText(QString("%1 / %2")
399 .arg(QString::number(m_pagenum),
400 QString::number(m_maxpage)));
401 }
402
403 ResultItem::resultList list = item->GetVideoList();
404 PopulateResultList(list);
406}
407
409{
411
412 QString message = tr("Timed out waiting for query to "
413 "finish. API might be down.");
414
415 if (!m_okPopup)
416 {
417 m_okPopup = new MythConfirmationDialog(m_popupStack, message, false);
418
419 if (m_okPopup->Create())
420 {
422 }
423 else
424 {
425 delete m_okPopup;
426 m_okPopup = nullptr;
427 }
428 }
429}
430
432{
433 for (const auto & result : std::as_const(list))
434 {
435 const QString& title = result->GetTitle();
436 auto *item = new MythUIButtonListItem(m_searchResultList, title,
437 QVariant::fromValue(result));
438 InfoMap metadataMap;
439 result->toMap(metadataMap);
440 item->SetTextFromMap(metadataMap);
441
442 if (!result->GetThumbnail().isEmpty())
443 {
444 QString dlfile = result->GetThumbnail();
445
446 if (dlfile.contains("%SHAREDIR%"))
447 {
448 dlfile.replace("%SHAREDIR%", GetShareDir());
449 item->SetImage(dlfile);
450 }
451 else
452 {
454
455 m_imageDownload->addThumb(result->GetTitle(),
456 result->GetThumbnail(),
457 QVariant::fromValue<uint>(pos));
458 }
459 }
460 }
461}
462
464{
465 return m_searchResultList->GetDataValue().value<ResultItem*>();
466}
467
469{
471
472 auto *searchedit = new SearchEditor(mainStack, "mythnetsearchedit");
473
474 if (searchedit->Create())
475 {
476 connect(searchedit, &NetEditorBase::ItemsChanged,
478
479 mainStack->AddScreen(searchedit);
480 }
481 else
482 {
483 delete searchedit;
484 }
485}
486
488{
490 if (m_grabberList.isEmpty())
492
493 LoadData();
494}
495
497{
498 auto *item = m_searchResultList->GetDataValue().value<ResultItem*>();
499
500 if (item && GetFocusWidget() == m_searchResultList)
501 {
503
504 if (m_downloadable)
505 {
506 if (item->GetDownloadable())
508 else
510 }
511 }
512 else if (GetFocusWidget() == m_siteList)
513 {
515 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
516 QString title = btn->GetText();
517
518 ResultItem res(title, sh->doTitle(title), // title, sortTitle
519 QString(), QString(), // subtitle, sortSubtitle
520 QString(), // description
521 QString(), QString(), QString(), QString(),
522 QDateTime(), nullptr, nullptr, -1, QString(), QStringList(),
523 QString(), QStringList(), 0, 0, QString(),
524 false, QStringList(), 0, 0, false);
525
526 SetTextAndThumbnail(btn, &res);
527 }
528}
529
531{
532 InfoMap metadataMap;
533 item->toMap(metadataMap);
534 SetTextFromMap(metadataMap);
535
536 SetThumbnail(btn);
537}
538
540{
541 if (m_thumbImage)
542 {
543 QString filename = btn->GetImageFilename();
544 if (filename.contains("%SHAREDIR%"))
545 filename.replace("%SHAREDIR%", GetShareDir());
546
547 if (!filename.isEmpty())
548 {
551 }
552 else
553 {
555 }
556 }
557}
558
559void NetSearch::customEvent(QEvent *event)
560{
561 if (event->type() == ThumbnailDLEvent::kEventType)
562 {
563 auto *tde = dynamic_cast<ThumbnailDLEvent *>(event);
564 if (tde == nullptr)
565 return;
566
567 ThumbnailData *data = tde->m_thumb;
568 if (!data)
569 return;
570
571 QString title = data->title;
572 QString file = data->url;
573 uint pos = data->data.value<uint>();
574
575 if (file.isEmpty() || !((uint)m_searchResultList->GetCount() >= pos))
576 return;
577
579
580 if (!item)
581 return;
582
583 if (item->GetText() == title)
584 item->SetImage(file);
585
586 if (m_searchResultList->GetItemCurrent() == item)
587 SetThumbnail(item);
588 }
589 else
590 {
592 }
593}
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:130
GrabberScript::scriptList m_grabberList
Definition: netbase.h:53
void DoPlayVideo()
Definition: netbase.cpp:326
void ShowWebVideo(void)
Definition: netbase.cpp:112
void customEvent(QEvent *event) override
Definition: netbase.cpp:231
MythUIStateType * m_downloadable
Definition: netbase.h:47
void SlotDeleteVideo(void)
Definition: netbase.cpp:189
MetadataImageDownload * m_imageDownload
Definition: netbase.h:50
void StreamWebVideo(void)
Definition: netbase.cpp:92
MythScreenStack * m_popupStack
Definition: netbase.h:48
MythUIImage * m_thumbImage
Definition: netbase.h:46
void DoDownloadAndPlay(void)
Definition: netbase.cpp:290
void ItemsChanged(void)
QNetworkReply * m_reply
Definition: netsearch.h:52
QNetworkAccessManager * m_netSearch
Definition: netsearch.h:51
void SkipPagesForward()
Definition: netsearch.cpp:356
void GetLastResults()
Definition: netsearch.cpp:310
void DoListRefresh()
Definition: netsearch.cpp:487
MythUIText * m_noSites
Definition: netsearch.h:46
void SetTextAndThumbnail(MythUIButtonListItem *btn, ResultItem *item)
Definition: netsearch.cpp:530
NetSearch(MythScreenStack *parent, const char *name=nullptr)
Definition: netsearch.cpp:33
void ShowMenu(void) override
Definition: netsearch.cpp:174
bool Create(void) override
Definition: netsearch.cpp:39
void LoadData(void) override
Definition: netsearch.cpp:107
void FillGrabberButtonList(void)
Definition: netsearch.cpp:251
uint m_maxpage
Definition: netsearch.h:58
void customEvent(QEvent *levent) override
Definition: netsearch.cpp:559
QString m_currentSearch
Definition: netsearch.h:54
QString m_currentCmd
Definition: netsearch.h:56
void SearchTimeout(Search *item)
Definition: netsearch.cpp:408
void SkipPagesBack()
Definition: netsearch.cpp:346
void GetMoreResults()
Definition: netsearch.cpp:328
void RunSearchEditor() const
Definition: netsearch.cpp:468
QString m_mythXML
Definition: netsearch.h:59
QString m_nextPageToken
Definition: netsearch.h:63
void SearchFinished(void)
Definition: netsearch.cpp:363
ResultItem * GetStreamItem() override
Definition: netsearch.cpp:463
MythUIProgressBar * m_progress
Definition: netsearch.h:48
MythUIButtonList * m_searchResultList
Definition: netsearch.h:41
MythUIButtonList * m_siteList
Definition: netsearch.h:42
void PopulateResultList(const ResultItem::resultList &list)
Definition: netsearch.cpp:431
void SlotItemChanged(void)
Definition: netsearch.cpp:496
~NetSearch() override
Definition: netsearch.cpp:97
void SetThumbnail(MythUIButtonListItem *btn)
Definition: netsearch.cpp:539
int m_currentGrabber
Definition: netsearch.h:55
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: netsearch.cpp:120
uint m_pagenum
Definition: netsearch.h:57
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:64
MythUIText * m_pageText
Definition: netsearch.h:45
MythConfirmationDialog * m_okPopup
Definition: netsearch.h:49
MythUITextEdit * m_search
Definition: netsearch.h:43
void DoSearch(void)
Definition: netsearch.cpp:266
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:463
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: compat.h:60
QString GetShareDir(void)
Definition: mythdirs.cpp:283
#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