MythTV master
netsearch.cpp
Go to the documentation of this file.
1// qt
2#include <QChar> // Fix Qt6 GCC SFINAE warning
3#include <QDateTime>
4#include <QFileInfo>
5#include <QImageReader>
6#include <QMetaType>
7#include <QString>
8
9// MythTV
10#include <libmythbase/mythdb.h>
26
27#include "netcommon.h"
28#include "netsearch.h"
29#include "rsseditor.h"
30#include "searcheditor.h"
31
32// ---------------------------------------------------
33
34NetSearch::NetSearch(MythScreenStack *parent, const char *name)
35 : NetBase(parent, name),
36 m_mythXML(GetMythXMLURL())
37{
38}
39
41{
42 // Load the theme for this screen
43 bool foundtheme = LoadWindowFromXML("netvision-ui.xml", "netsearch", this);
44
45 if (!foundtheme)
46 return false;
47
48 m_siteList = dynamic_cast<MythUIButtonList *> (GetChild("sites"));
49 m_searchResultList = dynamic_cast<MythUIButtonList *> (GetChild("results"));
50
51 m_pageText = dynamic_cast<MythUIText *> (GetChild("page"));
52 m_noSites = dynamic_cast<MythUIText *> (GetChild("nosites"));
53
54 m_thumbImage = dynamic_cast<MythUIImage *> (GetChild("preview"));
55
56 m_downloadable = dynamic_cast<MythUIStateType *> (GetChild("downloadable"));
57
58 m_progress = dynamic_cast<MythUIProgressBar *> (GetChild("progress"));
59
60 if (m_progress)
61 m_progress->SetVisible(false);
62
63 if (m_noSites)
64 m_noSites->SetVisible(false);
65
66 m_search = dynamic_cast<MythUITextEdit *> (GetChild("search"));
67
69 {
70 LOG(VB_GENERAL, LOG_ERR, "Theme is missing critical theme elements.");
71 return false;
72 }
73
75
76 // UI Hookups
80 this, &NetSearch::DoSearch);
85
87
89
90 return true;
91}
92
94{
96}
97
99{
100 if (m_netSearch)
101 {
102 m_netSearch->disconnect();
103 m_netSearch->deleteLater();
104 m_netSearch = nullptr;
105 }
106}
107
109{
111
112 if (m_grabberList.count() == 0 && m_noSites)
113 m_noSites->SetVisible(true);
114 else if (m_noSites)
115 m_noSites->SetVisible(false);
116
117 if (m_grabberList.isEmpty())
119}
120
121bool NetSearch::keyPressEvent(QKeyEvent *event)
122{
123 if (GetFocusWidget()->keyPressEvent(event))
124 return true;
125
126 QStringList actions;
127 bool handled = GetMythMainWindow()->TranslateKeyPress("Internet Video",
128 event, actions);
129
130 for (int i = 0; i < actions.size() && !handled; i++)
131 {
132 const QString& action = actions[i];
133 handled = true;
134
135 if (action == "MENU")
136 {
137 ShowMenu();
138 }
139 else if (action == "PAGELEFT" && m_pagenum > 1)
140 {
141 if (m_prevPageToken.isEmpty())
143 else
145 }
146 else if (action == "PAGERIGHT" && m_searchResultList->GetCount() > 0 &&
147 m_pagenum + 10 < m_maxpage)
148 {
149 if (m_nextPageToken.isEmpty())
151 else
153 }
154 else if (action == "PREVVIEW" && m_pagenum > 1)
155 {
157 }
158 else if (action == "NEXTVIEW" && m_searchResultList->GetCount() > 0 &&
160 {
162 }
163 else
164 {
165 handled = false;
166 }
167 }
168
169 if (!handled && MythScreenType::keyPressEvent(event))
170 handled = true;
171
172 return handled;
173}
174
176{
177 QString label = tr("Search Options");
178
179 auto *menuPopup = new MythDialogBox(label, m_popupStack,
180 "mythnetvisionmenupopup");
181
182 if (menuPopup->Create())
183 {
184 m_popupStack->AddScreen(menuPopup);
185
186 menuPopup->SetReturnEvent(this, "options");
187
188 if (m_searchResultList->GetCount() > 0)
189 {
190 ResultItem *item = GetStreamItem();
191 if (item)
192 {
193 if (item->GetDownloadable())
194 menuPopup->AddButton(tr("Stream Video"),
196 menuPopup->AddButton(tr("Open Web Link"), &NetSearch::ShowWebVideo);
197
198 QString filename = GetDownloadFilename(item->GetTitle(),
199 item->GetMediaURL());
200 bool exists = false;
201
202 if (filename.startsWith("myth://"))
204 else
206
207 if (item->GetDownloadable() &&
209 {
210 if (exists)
211 {
212 menuPopup->AddButton(tr("Play"),
213 qOverload<>(&NetSearch::DoPlayVideo));
214 }
215 else
216 {
217 menuPopup->AddButton(tr("Save This Video"),
219 }
220 }
221
222 if (item->GetDownloadable() &&
224 exists)
225 {
226 menuPopup->AddButton(tr("Delete"), &NetSearch::SlotDeleteVideo);
227 }
228 }
229 }
230
231 if (m_pagenum > 1)
232 menuPopup->AddButton(tr("Previous Page"), &NetSearch::GetLastResults);
234 menuPopup->AddButton(tr("Next Page"), &NetSearch::GetMoreResults);
235 if (m_pagenum > 1 && m_prevPageToken.isEmpty())
236 menuPopup->AddButton(tr("Skip 10 Pages Back"),
239 m_nextPageToken.isEmpty())
240 menuPopup->AddButton(tr("Skip 10 Pages Forward"),
242
243 menuPopup->AddButton(tr("Manage Search Scripts"),
245 }
246 else
247 {
248 delete menuPopup;
249 }
250}
251
253{
254 m_siteList->Reset();
255
256 for (const auto & g : std::as_const(m_grabberList))
257 {
258 auto *item = new MythUIButtonListItem(m_siteList, g->GetTitle());
259 item->SetText(g->GetTitle(), "title");
260 item->SetData(g->GetCommandline());
261 QString thumb = QString("%1mythnetvision/icons/%2")
262 .arg(GetShareDir(), g->GetImage());
263 item->SetImage(thumb);
264 }
265}
266
268{
270
271 int numScripts = m_siteList->GetCount();
272 for (int i = 0; i < numScripts; i++)
273 m_siteList->GetItemAt(i)->SetText("", "count");
274
275 if (m_pageText)
276 m_pageText->Reset();
277
278 m_pagenum = 1;
279 m_maxpage = 1;
280
281 QString cmd = m_siteList->GetDataValue().toString();
282 QString grabber = m_siteList->GetItemCurrent()->GetText();
283 QString query = m_search->GetText();
284
285 if (query.isEmpty())
286 return;
287
288 QFileInfo fi(cmd);
289 m_currentCmd = fi.fileName();
291 m_currentSearch = query;
292
293 QString title = tr("Searching %1 for \"%2\"...")
294 .arg(grabber, query);
295 OpenBusyPopup(title);
296
297 if (!m_netSearch)
298 {
299 m_netSearch = new QNetworkAccessManager(this);
300 connect(m_netSearch, &QNetworkAccessManager::finished,
302 }
303
305 QUrl req(init.toEncoded(), QUrl::TolerantMode);
306 LOG(VB_GENERAL, LOG_INFO,
307 QString("Using Search URL %1").arg(req.toString()));
308 m_reply = m_netSearch->get(QNetworkRequest(req));
309}
310
312{
314
315 m_pagenum--;
316
317 QString title = tr("Changing to page %1 of search \"%2\"...")
318 .arg(QString::number(m_pagenum), m_currentSearch);
319 OpenBusyPopup(title);
320
321 QString page = m_prevPageToken.isEmpty() ? QString::number(m_pagenum) :
323
324 QUrl req =
326 m_reply = m_netSearch->get(QNetworkRequest(req));
327}
328
330{
332
333 m_pagenum++;
334
335 QString title = tr("Changing to page %1 of search \"%2\"...")
336 .arg(QString::number(m_pagenum), m_currentSearch);
337 OpenBusyPopup(title);
338
339 QString page = m_nextPageToken.isEmpty() ? QString::number(m_pagenum) :
341
342 QUrl req =
344 m_reply = m_netSearch->get(QNetworkRequest(req));
345}
346
348{
349 if (m_pagenum < 11)
350 m_pagenum = 2;
351 else
352 m_pagenum = m_pagenum - 10 + 1;
353
355}
356
358{
359 m_pagenum = m_pagenum + 10 - 1;
360
362}
363
365{
367
368 auto *item = new Search();
369 QByteArray data = m_reply->readAll();
370 item->SetData(data);
371
372 item->process();
373
374 uint searchresults = item->numResults();
375 uint returned = item->numReturned();
376 uint firstitem = item->numIndex();
377
378 m_nextPageToken = item->nextPageToken();
379 m_prevPageToken = item->prevPageToken();
380
381 if (returned == 0)
382 return;
383
385 SetText(QString::number(searchresults), "count");
386
387 if (firstitem + returned == searchresults)
388 {
390 }
391 else
392 {
393 m_maxpage = searchresults / returned; // Whole pages
394 if (searchresults % returned != 0) // Partial page?
395 m_maxpage++;
396 }
397 if (m_pageText && m_maxpage > 0 && m_pagenum > 0)
398 {
399 m_pageText->SetText(QString("%1 / %2")
400 .arg(QString::number(m_pagenum),
401 QString::number(m_maxpage)));
402 }
403
404 ResultItem::resultList list = item->GetVideoList();
405 PopulateResultList(list);
407}
408
410{
412
413 QString message = tr("Timed out waiting for query to "
414 "finish. API might be down.");
415
416 if (!m_okPopup)
417 {
418 m_okPopup = new MythConfirmationDialog(m_popupStack, message, false);
419
420 if (m_okPopup->Create())
421 {
423 }
424 else
425 {
426 delete m_okPopup;
427 m_okPopup = nullptr;
428 }
429 }
430}
431
433{
434 for (const auto & result : std::as_const(list))
435 {
436 const QString& title = result->GetTitle();
437 auto *item = new MythUIButtonListItem(m_searchResultList, title,
438 QVariant::fromValue(result));
439 InfoMap metadataMap;
440 result->toMap(metadataMap);
441 item->SetTextFromMap(metadataMap);
442
443 if (!result->GetThumbnail().isEmpty())
444 {
445 QString dlfile = result->GetThumbnail();
446
447 if (dlfile.contains("%SHAREDIR%"))
448 {
449 dlfile.replace("%SHAREDIR%", GetShareDir());
450 item->SetImage(dlfile);
451 }
452 else
453 {
455
456 m_imageDownload->addThumb(result->GetTitle(),
457 result->GetThumbnail(),
458 QVariant::fromValue<uint>(pos));
459 }
460 }
461 }
462}
463
465{
466 return m_searchResultList->GetDataValue().value<ResultItem*>();
467}
468
470{
472
473 auto *searchedit = new SearchEditor(mainStack, "mythnetsearchedit");
474
475 if (searchedit->Create())
476 {
477 connect(searchedit, &NetEditorBase::ItemsChanged,
479
480 mainStack->AddScreen(searchedit);
481 }
482 else
483 {
484 delete searchedit;
485 }
486}
487
489{
491 if (m_grabberList.isEmpty())
493
494 LoadData();
495}
496
498{
499 auto *item = m_searchResultList->GetDataValue().value<ResultItem*>();
500
501 if (item && GetFocusWidget() == m_searchResultList)
502 {
504
505 if (m_downloadable)
506 {
507 if (item->GetDownloadable())
509 else
511 }
512 }
513 else if (GetFocusWidget() == m_siteList)
514 {
516 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
517 QString title = btn->GetText();
518
519 ResultItem res(title, sh->doTitle(title), // title, sortTitle
520 QString(), QString(), // subtitle, sortSubtitle
521 QString(), // description
522 QString(), QString(), QString(), QString(),
523 QDateTime(), nullptr, nullptr, -1, QString(), QStringList(),
524 QString(), QStringList(), 0, 0, QString(),
525 false, QStringList(), 0, 0, false);
526
527 SetTextAndThumbnail(btn, &res);
528 }
529}
530
532{
533 InfoMap metadataMap;
534 item->toMap(metadataMap);
535 SetTextFromMap(metadataMap);
536
537 SetThumbnail(btn);
538}
539
541{
542 if (m_thumbImage)
543 {
544 QString filename = btn->GetImageFilename();
545 if (filename.contains("%SHAREDIR%"))
546 filename.replace("%SHAREDIR%", GetShareDir());
547
548 if (!filename.isEmpty())
549 {
552 }
553 else
554 {
556 }
557 }
558}
559
560void NetSearch::customEvent(QEvent *event)
561{
562 if (event->type() == ThumbnailDLEvent::kEventType)
563 {
564 auto *tde = dynamic_cast<ThumbnailDLEvent *>(event);
565 if (tde == nullptr)
566 return;
567
568 ThumbnailData *data = tde->m_thumb;
569 if (!data)
570 return;
571
572 QString title = data->title;
573 QString file = data->url;
574 uint pos = data->data.value<uint>();
575
576 if (file.isEmpty() || !((uint)m_searchResultList->GetCount() >= pos))
577 return;
578
580
581 if (!item)
582 return;
583
584 if (item->GetText() == title)
585 item->SetImage(file);
586
587 if (m_searchResultList->GetItemCurrent() == item)
588 SetThumbnail(item);
589 }
590 else
591 {
593 }
594}
595
596#include "moc_netsearch.cpp"
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:99
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:357
void GetLastResults()
Definition: netsearch.cpp:311
void DoListRefresh()
Definition: netsearch.cpp:488
MythUIText * m_noSites
Definition: netsearch.h:46
void SetTextAndThumbnail(MythUIButtonListItem *btn, ResultItem *item)
Definition: netsearch.cpp:531
NetSearch(MythScreenStack *parent, const char *name=nullptr)
Definition: netsearch.cpp:34
void ShowMenu(void) override
Definition: netsearch.cpp:175
bool Create(void) override
Definition: netsearch.cpp:40
void LoadData(void) override
Definition: netsearch.cpp:108
void FillGrabberButtonList(void)
Definition: netsearch.cpp:252
uint m_maxpage
Definition: netsearch.h:58
void customEvent(QEvent *levent) override
Definition: netsearch.cpp:560
QString m_currentSearch
Definition: netsearch.h:54
QString m_currentCmd
Definition: netsearch.h:56
void SearchTimeout(Search *item)
Definition: netsearch.cpp:409
void SkipPagesBack()
Definition: netsearch.cpp:347
void GetMoreResults()
Definition: netsearch.cpp:329
void RunSearchEditor() const
Definition: netsearch.cpp:469
QString m_mythXML
Definition: netsearch.h:59
QString m_nextPageToken
Definition: netsearch.h:63
void SearchFinished(void)
Definition: netsearch.cpp:364
ResultItem * GetStreamItem() override
Definition: netsearch.cpp:464
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:432
void SlotItemChanged(void)
Definition: netsearch.cpp:497
~NetSearch() override
Definition: netsearch.cpp:98
void SetThumbnail(MythUIButtonListItem *btn)
Definition: netsearch.cpp:540
int m_currentGrabber
Definition: netsearch.h:55
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: netsearch.cpp:121
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:93
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:267
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:280
#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