MythTV master
videodlg.cpp
Go to the documentation of this file.
1// C++
2#include <algorithm>
3#include <chrono>
4#include <functional> //binary_negate
5#include <map>
6#include <memory>
7#include <set>
8
9// Qt
10#include <QApplication>
11#include <QDir>
12#include <QFile>
13#include <QFileInfo>
14#include <QList>
15#include <QTimer>
16#include <QUrl>
17
18// MythTV
30#include "libmythmetadata/metadataimagedownload.h" // for ImageDLFailureEvent
47
48// MythFrontend
49#include "editvideometadata.h"
50#include "playbackstate.h"
51#include "videodlg.h"
52#include "videofileassoc.h"
53#include "videofilter.h"
54#include "videolist.h"
56#include "videoplayercommand.h"
57#include "videoplayersettings.h"
58#include "videopopups.h"
59
60#define LOC_MML QString("Manual Metadata Lookup: ")
61
62static const QString sLocation = "MythVideo";
63
64namespace
65{
66 bool IsValidDialogType(int num)
67 {
68 for (int i = 1; i <= VideoDialog::dtLast - 1; i <<= 1)
69 if (num == i) return true;
70 return false;
71 }
72
73 class ParentalLevelNotifyContainer : public QObject
74 {
75 Q_OBJECT
76
77 signals:
79
80 public:
81 explicit ParentalLevelNotifyContainer(QObject *lparent = nullptr) :
82 QObject(lparent)
83 {
84 connect(&m_levelCheck,
86 this, &ParentalLevelNotifyContainer::OnResultReady);
87 }
88
89 const ParentalLevel &GetLevel() const { return m_level; }
90
91 void SetLevel(const ParentalLevel& level)
92 {
93 m_levelCheck.Check(m_level.GetLevel(), level.GetLevel());
94 }
95
96 private slots:
97 void OnResultReady(bool passwordValid, ParentalLevel::Level newLevel)
98 {
99 ParentalLevel lastLevel = m_level;
100 if (passwordValid)
101 {
102 m_level = newLevel;
103 }
104
105 if (m_level.GetLevel() == ParentalLevel::plNone)
106 {
108 }
109
110 if (lastLevel != m_level)
111 {
112 emit SigLevelChanged();
113 }
114 }
115
116 private:
119 };
120
122 {
123 if (item && item->GetData().canConvert<MythGenericTree *>())
124 return item->GetData().value<MythGenericTree *>();
125
126 return nullptr;
127 }
128
130 {
131 if (node && node->GetData().canConvert<TreeNodeData>())
132 return node->GetData().value<TreeNodeData>().GetMetadata();
133
134 return nullptr;
135 }
136
137 bool GetLocalVideoImage(const QString &video_uid, const QString &filename,
138 const QStringList &in_dirs, QString &image,
139 QString title, int season,
140 const QString &host, const QString& sgroup,
141 int episode = 0, bool isScreenshot = false)
142 {
143 QStringList search_dirs(in_dirs);
144 QFileInfo qfi(filename);
145 search_dirs += qfi.absolutePath();
146 if (title.contains("/"))
147 title.replace("/", "-");
148
149 const QString base_name = qfi.completeBaseName();
150 QList<QByteArray> image_types = QImageReader::supportedImageFormats();
151
152 using image_type_list = std::set<QString>;
153 image_type_list image_exts;
154
155 QString suffix;
156
157 if (sgroup == "Coverart")
158 suffix = "coverart";
159 if (sgroup == "Fanart")
160 suffix = "fanart";
161 if (sgroup == "Screenshots")
162 suffix = "screenshot";
163 if (sgroup == "Banners")
164 suffix = "banner";
165
166 for (const auto & itype : std::as_const(image_types))
167 image_exts.insert(QString(itype).toLower());
168
169 if (!host.isEmpty())
170 {
171 QStringList hostFiles;
172
173 StorageGroup::remoteGetFileList(host, "", &hostFiles, sgroup, true);
174 const QString hntm("%2.%3");
175
176 for (const auto & ext : image_exts)
177 {
178 QStringList sfn;
179 if (episode > 0 || season > 0)
180 {
181 if (isScreenshot)
182 {
183 sfn += hntm.arg(QString("%1 Season %2x%3_%4")
184 .arg(title, QString::number(season),
185 QString::number(episode), suffix),
186 ext);
187 }
188 else
189 {
190 sfn += hntm.arg(QString("%1 Season %2_%3")
191 .arg(title, QString::number(season),
192 suffix),
193 ext);
194 }
195
196 }
197 else
198 {
199 sfn += hntm.arg(base_name + QString("_%1").arg(suffix),
200 ext);
201 sfn += hntm.arg(video_uid + QString("_%1").arg(suffix),
202 ext);
203 }
204
205 for (const auto & str : std::as_const(sfn))
206 {
207 if (hostFiles.contains(str))
208 {
209 image = str;
210 return true;
211 }
212 }
213 }
214 }
215
216 const QString fntm("%1/%2.%3");
217
218 for (const auto & dir : std::as_const(search_dirs))
219 {
220 if (dir.isEmpty()) continue;
221
222 for (const auto & ext : image_exts)
223 {
224 QStringList sfn;
225 if (season > 0 || episode > 0)
226 {
227 if (isScreenshot)
228 {
229 sfn += fntm.arg(dir,
230 QString("%1 Season %2x%3_%4")
231 .arg(title, QString::number(season),
232 QString::number(episode),
233 suffix),
234 ext);
235 }
236 else
237 {
238 sfn += fntm.arg(dir,
239 QString("%1 Season %2_%3")
240 .arg(title, QString::number(season),
241 suffix),
242 ext);
243 }
244 }
245 if (!isScreenshot)
246 {
247 sfn += fntm.arg(dir,
248 base_name + QString("_%1").arg(suffix),
249 ext);
250 sfn += fntm.arg(dir,
251 video_uid + QString("_%1").arg(suffix),
252 ext);
253 }
254
255 for (const auto & file : std::as_const(sfn))
256 {
257 if (QFile::exists(file))
258 {
259 image = file;
260 return true;
261 }
262 }
263 }
264 }
265
266 return false;
267 }
268
269 void PlayVideo(const QString &filename,
270 const VideoMetadataListManager &video_list, bool useAltPlayer = false)
271 {
272 const int WATCHED_WATERMARK = 10000; // Less than this and the chain of
273 // videos will not be followed when
274 // playing.
275
277
278 if (!item) return;
279
280 QElapsedTimer playing_time;
281
282 while (item)
283 {
284 playing_time.start();
285
286 if (useAltPlayer)
288 else
290
291 if (!playing_time.hasExpired(WATCHED_WATERMARK))
292 return;
293
294 item = (item->GetChildID() > 0)
295 ? video_list.byID(item->GetChildID())
296 : nullptr;
297 }
298 }
299
300 class FanartLoader: public QObject
301 {
302 Q_OBJECT
303
304 public:
305 FanartLoader() = default;
306 ~FanartLoader() override
307 {
308 m_fanartTimer.stop();
309 m_fanartTimer.disconnect(this);
310 }
311
312 void LoadImage(const QString &filename, MythUIImage *image)
313 {
314 if (!m_bConnected)
315 {
316 connect(&m_fanartTimer, &QTimer::timeout, this, &FanartLoader::fanartLoad);
317 m_bConnected = true;
318 }
319
320 bool wasActive = m_fanartTimer.isActive();
321 if (filename.isEmpty())
322 {
323 if (wasActive)
324 m_fanartTimer.stop();
325
326 image->Reset();
327 m_itemsPast++;
328 }
329 else
330 {
331 QMutexLocker locker(&m_fanartLock);
332 m_fanart = image;
333 if (filename != m_fanart->GetFilename())
334 {
335 if (wasActive)
336 m_fanartTimer.stop();
337
338 if (m_itemsPast > 2)
339 m_fanart->Reset();
340
341 m_fanart->SetFilename(filename);
342 m_fanartTimer.setSingleShot(true);
343 m_fanartTimer.start(300ms);
344
345 if (wasActive)
346 m_itemsPast++;
347 else
348 m_itemsPast = 0;
349 }
350 else
351 {
352 m_itemsPast = 0;
353 }
354 }
355 }
356
357 protected slots:
358 void fanartLoad(void)
359 {
360 QMutexLocker locker(&m_fanartLock);
361 m_fanart->Load();
362 }
363
364 private:
365 int m_itemsPast {0};
367 MythUIImage *m_fanart {nullptr};
369 bool m_bConnected {false};
370 };
371
372 std::unique_ptr<FanartLoader> fanartLoader;
373
375 {
376 virtual void handleText(const QString &name, const QString &value) = 0;
377 virtual void handleState(const QString &name, const QString &value) = 0;
378 virtual void handleImage(const QString &name,
379 const QString &filename) = 0;
380 };
381
383 {
384 public:
385 explicit ScreenCopyDest(MythScreenType *screen) : m_screen(screen) {}
386
387 void handleText(const QString &name, const QString &value) override // CopyMetadataDestination
388 {
389 CheckedSet(m_screen, name, value);
390 }
391
392 void handleState(const QString &name, const QString &value) override // CopyMetadataDestination
393 {
394 handleText(name, value);
395 }
396
397 void handleImage(const QString &name, const QString &filename) override // CopyMetadataDestination
398 {
399 MythUIImage *image = nullptr;
400 UIUtilW::Assign(m_screen, image, name);
401 if (image)
402 {
403 if (name != "fanart")
404 {
405 if (!filename.isEmpty())
406 {
407 image->SetFilename(filename);
408 image->Load();
409 }
410 else
411 {
412 image->Reset();
413 }
414 }
415 else
416 {
417 if (fanartLoader == nullptr)
418 fanartLoader = std::make_unique<FanartLoader>();
419 fanartLoader->LoadImage(filename, image);
420 }
421 }
422 }
423
424 private:
425 MythScreenType *m_screen {nullptr};
426 };
427
429 {
430 public:
432 m_item(item) {}
433
434 void handleText(const QString &name, const QString &value) override // CopyMetadataDestination
435 {
436 m_item->SetText(value, name);
437 }
438
439 void handleState(const QString &name, const QString &value) override // CopyMetadataDestination
440 {
441 m_item->DisplayState(value, name);
442 }
443
444 void handleImage([[maybe_unused]] const QString &name,
445 [[maybe_unused]] const QString &filename) override // CopyMetadataDestination
446 {
447 }
448
449 private:
450 MythUIButtonListItem *m_item {nullptr};
451 };
452
453 void CopyMetadataToUI(const VideoMetadata *metadata,
455 {
456 using valuelist = std::map<QString, QString>;
457 valuelist tmp;
458
459 if (metadata)
460 {
461 QString coverfile;
462 if ((metadata->IsHostSet()
463 && !metadata->GetCoverFile().startsWith("/"))
464 && !metadata->GetCoverFile().isEmpty()
465 && !IsDefaultCoverFile(metadata->GetCoverFile()))
466 {
467 coverfile = StorageGroup::generate_file_url("Coverart", metadata->GetHost(),
468 metadata->GetCoverFile());
469 }
470 else
471 {
472 coverfile = metadata->GetCoverFile();
473 }
474
475 if (!IsDefaultCoverFile(coverfile))
476 tmp["coverart"] = coverfile;
477
478 tmp["coverfile"] = coverfile;
479
480 QString screenshotfile;
481 if (metadata->IsHostSet() && !metadata->GetScreenshot().startsWith("/")
482 && !metadata->GetScreenshot().isEmpty())
483 {
484 screenshotfile = StorageGroup::generate_file_url("Screenshots",
485 metadata->GetHost(), metadata->GetScreenshot());
486 }
487 else
488 {
489 screenshotfile = metadata->GetScreenshot();
490 }
491
492 if (!IsDefaultScreenshot(screenshotfile))
493 tmp["screenshot"] = screenshotfile;
494
495 tmp["screenshotfile"] = screenshotfile;
496
497 QString bannerfile;
498 if (metadata->IsHostSet() && !metadata->GetBanner().startsWith("/")
499 && !metadata->GetBanner().isEmpty())
500 {
501 bannerfile = StorageGroup::generate_file_url("Banners", metadata->GetHost(),
502 metadata->GetBanner());
503 }
504 else
505 {
506 bannerfile = metadata->GetBanner();
507 }
508
509 if (!IsDefaultBanner(bannerfile))
510 tmp["banner"] = bannerfile;
511
512 tmp["bannerfile"] = bannerfile;
513
514 QString fanartfile;
515 if (metadata->IsHostSet() && !metadata->GetFanart().startsWith("/")
516 && !metadata->GetFanart().isEmpty())
517 {
518 fanartfile = StorageGroup::generate_file_url("Fanart", metadata->GetHost(),
519 metadata->GetFanart());
520 }
521 else
522 {
523 fanartfile = metadata->GetFanart();
524 }
525
526 if (!IsDefaultFanart(fanartfile))
527 tmp["fanart"] = fanartfile;
528
529 tmp["fanartfile"] = fanartfile;
530
531 tmp["trailerstate"] = TrailerToState(metadata->GetTrailer());
532 tmp["studiostate"] = metadata->GetStudio();
533 tmp["userratingstate"] =
534 QString::number((int)(metadata->GetUserRating()));
535 tmp["watchedstate"] = WatchedToState(metadata->GetWatched());
536
537 tmp["videolevel"] = ParentalLevelToState(metadata->GetShowLevel());
538 }
539
540 struct helper
541 {
542 helper(valuelist &values, CopyMetadataDestination &d) :
543 m_vallist(values), m_dest(d) {}
544
545 void handleImage(const QString &name)
546 {
547 m_dest.handleImage(name, m_vallist[name]);
548 }
549
550 void handleState(const QString &name)
551 {
552 m_dest.handleState(name, m_vallist[name]);
553 }
554 private:
555 valuelist &m_vallist;
557 };
558
559 helper h(tmp, dest);
560
561 h.handleImage("coverart");
562 h.handleImage("screenshot");
563 h.handleImage("banner");
564 h.handleImage("fanart");
565
566 h.handleState("trailerstate");
567 h.handleState("userratingstate");
568 h.handleState("watchedstate");
569 h.handleState("videolevel");
570 }
571
572 void CopyPlaybackStateToUI(const PlaybackState &playbackState,
573 const VideoMetadata *metadata,
574 MythUIButtonListItem *item = nullptr,
575 MythScreenType *screen = nullptr)
576 {
577 if (!metadata || (!item && !screen))
578 {
579 return;
580 }
581
582 const auto *const bookmarkState = playbackState.HasBookmark(metadata->GetFilename()) ? "yes" : "no";
583 const auto watchedPercent = playbackState.GetWatchedPercent(metadata->GetFilename());
584 const bool showProgress = watchedPercent && (playbackState.AlwaysShowWatchedProgress() || !metadata->GetWatched());
585 if (item)
586 {
587 item->DisplayState(bookmarkState, "bookmarkstate");
588 item->SetProgress1(0, showProgress ? 100 : 0, watchedPercent);
589 }
590 if (screen)
591 {
592 CheckedSet(screen, "bookmarkstate", bookmarkState);
593 auto *watchedProgress = dynamic_cast<MythUIProgressBar *>(screen->GetChild("watchedprogressbar"));
594 if (watchedProgress)
595 {
596 watchedProgress->Set(0, showProgress ? 100 : 0, watchedPercent);
597 }
598 }
599 }
600}
601
602
604{
605 Q_OBJECT
606
607 public:
608 static bool Exists()
609 {
610 // TODO: Add ability to theme loader to do this a better way.
611 return LoadWindowFromXML("video-ui.xml", kWindowName, nullptr);
612 }
613
614 public:
616 const VideoMetadataListManager &listManager, PlaybackState &playbackState) :
617 MythScreenType(lparent, kWindowName), m_metadata(metadata),
618 m_listManager(listManager), m_playbackState(playbackState)
619 {
620 }
621
622 bool Create() override // MythScreenType
623 {
624 if (!LoadWindowFromXML("video-ui.xml", kWindowName, this))
625 return false;
626
627 UIUtilW::Assign(this, m_playButton, "play_button");
628 UIUtilW::Assign(this, m_doneButton, "done_button");
629
630 if (m_playButton)
632
633 if (m_doneButton)
635
637
640
641 InfoMap metadataMap;
642 m_metadata->toMap(metadataMap);
643 SetTextFromMap(metadataMap);
644
645 ScreenCopyDest dest(this);
648
649 return true;
650 }
651
652 private slots:
653 void OnPlay()
654 {
656 }
657
658 void OnDone()
659 {
660 // TODO: Close() can do horrible things, this will pop
661 // our screen, delete us, and return here.
662 Close();
663 }
664
665 private:
666 bool OnKeyAction(const QStringList &actions)
667 {
668 bool handled = false;
669 for (const auto & action : std::as_const(actions))
670 {
671 handled = true;
672 if (action == "SELECT" || action == "PLAYBACK")
673 OnPlay();
674 else
675 handled = false;
676 }
677
678 return handled;
679 }
680
681 public:
682 bool keyPressEvent(QKeyEvent *levent) override // MythScreenType
683 {
685 return true;
686
687 QStringList actions;
688 bool handled = GetMythMainWindow()->TranslateKeyPress("Video",
689 levent, actions);
690 if (!handled && !OnKeyAction(actions))
691 {
692 handled = GetMythMainWindow()->TranslateKeyPress("TV Frontend",
693 levent, actions);
694 OnKeyAction(actions);
695 }
696 return handled;
697 }
698
699 private:
700 static const char * const kWindowName;
704
707};
708
709const char * const ItemDetailPopup::kWindowName = "itemdetailpopup";
710
712{
713 private:
714 using parental_level_map = std::list<std::pair<QString, ParentalLevel::Level> >;
715
716 static bool rating_to_pl_greater(const parental_level_map::value_type &lhs,
717 const parental_level_map::value_type &rhs)
718 {
719 return lhs.first.length() >= rhs.first.length();
720 };
721
723
724 public:
727 m_videoList(videoList), m_type(type), m_browse(browse)
728 {
729 if (gCoreContext->GetBoolSetting("mythvideo.ParentalLevelFromRating", false))
730 {
732 sl.GetLevel() <= ParentalLevel::plHigh && sl.good(); ++sl)
733 {
734 QString ratingstring =
735 gCoreContext->GetSetting(QString("mythvideo.AutoR2PL%1")
736 .arg(sl.GetLevel()));
737 QStringList ratings =
738 ratingstring.split(':', Qt::SkipEmptyParts);
739 auto to_pl = [sl](const auto & rating)
740 { return parental_level_map::value_type(rating, sl.GetLevel()); };
741 std::ranges::transform(std::as_const(ratings),
742 std::back_inserter(m_ratingToPl), to_pl);
743 }
745 }
746
748 gCoreContext->GetBoolSetting("mythvideo.VideoTreeRemember", false);
749
750 m_isFileBrowser = gCoreContext->GetBoolSetting("VideoDialogNoDB", false);
751 m_groupType = gCoreContext->GetNumSetting("mythvideo.db_group_type", 0);
752
754 gCoreContext->GetBoolSetting("mythvideo.EnableAlternatePlayer");
755
756 m_autoMeta = gCoreContext->GetBoolSetting("mythvideo.AutoMetaDataScan", true);
757
758 m_artDir = gCoreContext->GetSetting("VideoArtworkDir");
759 m_sshotDir = gCoreContext->GetSetting("mythvideo.screenshotDir");
760 m_fanDir = gCoreContext->GetSetting("mythvideo.fanartDir");
761 m_banDir = gCoreContext->GetSetting("mythvideo.bannerDir");
762 }
763
765 {
766 delete m_scanner;
767
768 if (m_rememberPosition && !m_lastTreeNodePath.isEmpty())
769 {
770 gCoreContext->SaveSetting("mythvideo.VideoTreeLastActive",
772 }
773 }
774
776 {
777 if (metadata && !m_ratingToPl.empty())
778 {
779 QString rating = metadata->GetRating();
780 for (auto p = m_ratingToPl.begin();
781 !rating.isEmpty() && p != m_ratingToPl.end(); ++p)
782 {
783 if (rating.indexOf(p->first) != -1)
784 {
785 metadata->SetShowLevel(p->second);
786 break;
787 }
788 }
789 }
790 }
791
792 static void DelayVideoListDestruction(const VideoListPtr& videoList)
793 {
794 m_savedPtr = new VideoListDeathDelay(videoList);
795 }
796
797 public:
798 ParentalLevelNotifyContainer m_parentalLevel;
799 bool m_switchingLayout {false};
800
802
803 bool m_firstLoadPass {true};
804
805 bool m_rememberPosition {false};
806
808
811
812 bool m_treeLoaded {false};
813
814 bool m_isFileBrowser {false};
815 int m_groupType {0};
816 bool m_isFlatList {false};
817 bool m_altPlayerEnabled {false};
820
821 bool m_autoMeta {true};
822
823 QString m_artDir;
824 QString m_sshotDir;
825 QString m_fanDir;
826 QString m_banDir;
828
830 QMap<QString, int> m_notifications;
831
833
834 private:
836};
837
839
841{
842 public:
844 m_savedList(toSave)
845 {
846 }
847
849 {
850 return m_savedList;
851 }
852
853 private:
855};
856
858 QObject(QCoreApplication::instance()),
859 m_d(new VideoListDeathDelayPrivate(toSave))
860{
861 QTimer::singleShot(kDelayTimeMS, this, &VideoListDeathDelay::OnTimeUp);
862}
863
865{
866 delete m_d;
867}
868
870{
871 return m_d->GetSaved();
872}
873
875{
876 deleteLater();
877}
878
880{
882}
883
884VideoDialog::VideoDialog(MythScreenStack *lparent, const QString& lname,
885 const VideoListPtr& video_list, DialogType type, BrowseType browse)
886 : MythScreenType(lparent, lname),
887 m_popupStack(GetMythMainWindow()->GetStack("popup stack")),
888 m_mainStack(GetMythMainWindow()->GetMainStack()),
889 m_metadataFactory(new MetadataFactory(this)),
890 m_d(new VideoDialogPrivate(video_list, type, browse))
891{
893 lname));
894
896 GetNumSetting("VideoDefaultParentalLevel",
898
901 // Get notified when playback stopped, so we can update watched progress
904}
905
907{
909 auto *item = GetItemCurrent();
910 const auto *metadata = GetMetadata(item);
911 if (metadata && metadata->GetFilename() == filename)
912 {
913 UpdateText(item);
914 }
915}
916
918{
919 auto *item = GetItemCurrent();
920 const auto *metadata = GetMetadata(item);
921 if (metadata)
922 {
923 m_d->m_playbackState.Update(metadata->GetFilename());
924 }
925 UpdateText(item);
926 UpdateWatchedState(item);
927}
928
929
931{
934
935 SavePosition();
936
937 delete m_d;
938}
939
941{
943
944 if (m_d->m_type == DLG_TREE)
945 {
947 if (node)
948 m_d->m_lastTreeNodePath = node->getRouteByString().join("\n");
949 }
950 else if (m_d->m_type == DLG_BROWSER || m_d->m_type == DLG_GALLERY)
951 {
953 if (item)
954 {
956 if (node)
957 m_d->m_lastTreeNodePath = node->getRouteByString().join("\n");
958 }
959 }
960
961 gCoreContext->SaveSetting("mythvideo.VideoTreeLastActive", m_d->m_lastTreeNodePath);
962}
963
965{
966 if (m_d->m_type == DLG_DEFAULT)
967 {
968 m_d->m_type = static_cast<DialogType>(
969 gCoreContext->GetNumSetting("Default MythVideo View", DLG_GALLERY));
970 m_d->m_browse = static_cast<BrowseType>(
971 gCoreContext->GetNumSetting("mythvideo.db_group_type", BRS_FOLDER));
972 }
973
975 {
977 }
978
979 QString windowName = "videogallery";
980 bool flatlistDefault = false;
981
982 switch (m_d->m_type)
983 {
984 case DLG_BROWSER:
985 windowName = "browser";
986 flatlistDefault = true;
987 break;
988 case DLG_GALLERY:
989 windowName = "gallery";
990 break;
991 case DLG_TREE:
992 windowName = "tree";
993 break;
994 case DLG_MANAGER:
996 gCoreContext->GetBoolSetting("mythvideo.db_folder_view", true);
997 windowName = "manager";
998 flatlistDefault = true;
999 break;
1000 case DLG_DEFAULT:
1001 default:
1002 break;
1003 }
1004
1005 switch (m_d->m_browse)
1006 {
1007 case BRS_GENRE:
1009 break;
1010 case BRS_CATEGORY:
1012 break;
1013 case BRS_YEAR:
1015 break;
1016 case BRS_DIRECTOR:
1018 break;
1019 case BRS_STUDIO:
1021 break;
1022 case BRS_CAST:
1024 break;
1025 case BRS_USERRATING:
1027 break;
1028 case BRS_INSERTDATE:
1030 break;
1031 case BRS_TVMOVIE:
1033 break;
1034 case BRS_FOLDER:
1035 default:
1037 break;
1038 }
1039
1040 m_d->m_isFlatList =
1041 gCoreContext->GetBoolSetting(QString("mythvideo.folder_view_%1")
1042 .arg(m_d->m_type), flatlistDefault);
1043
1044 if (!LoadWindowFromXML("video-ui.xml", windowName, this))
1045 return false;
1046
1047 bool err = false;
1048 if (m_d->m_type == DLG_TREE)
1049 UIUtilE::Assign(this, m_videoButtonTree, "videos", &err);
1050 else
1051 UIUtilE::Assign(this, m_videoButtonList, "videos", &err);
1052
1053 UIUtilW::Assign(this, m_titleText, "title");
1054 UIUtilW::Assign(this, m_novideoText, "novideos");
1055 UIUtilW::Assign(this, m_positionText, "position");
1056 UIUtilW::Assign(this, m_crumbText, "breadcrumbs");
1057
1058 UIUtilW::Assign(this, m_coverImage, "coverart");
1059 UIUtilW::Assign(this, m_screenshot, "screenshot");
1060 UIUtilW::Assign(this, m_banner, "banner");
1061 UIUtilW::Assign(this, m_fanart, "fanart");
1062
1063 UIUtilW::Assign(this, m_trailerState, "trailerstate");
1064 UIUtilW::Assign(this, m_parentalLevelState, "parentallevel");
1065 UIUtilW::Assign(this, m_watchedState, "watchedstate");
1066 UIUtilW::Assign(this, m_studioState, "studiostate");
1067 UIUtilW::Assign(this, m_bookmarkState, "bookmarkstate");
1068
1069 if (err)
1070 {
1071 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen '" + windowName + "'");
1072 return false;
1073 }
1074
1075 CheckedSet(m_trailerState, "None");
1077 CheckedSet(m_watchedState, "None");
1078 CheckedSet(m_studioState, "None");
1079 CheckedSet(m_bookmarkState, "None");
1080
1082
1083 if (m_d->m_type == DLG_TREE)
1084 {
1086
1095 }
1096 else
1097 {
1099
1106 }
1107
1108 return true;
1109}
1110
1112{
1113 connect(&m_d->m_parentalLevel, &ParentalLevelNotifyContainer::SigLevelChanged,
1115}
1116
1118{
1119 reloadData();
1120 // We only want to prompt once, on startup, hence this is done in Load()
1121 if (m_d->m_rootNode->childCount() == 1 &&
1123 PromptToScan();
1124}
1125
1131{
1132 fetchVideos();
1133 loadData();
1134
1137
1138 bool noFiles = (m_d->m_rootNode->childCount() == 1 &&
1140
1141 if (m_novideoText)
1142 m_novideoText->SetVisible(noFiles);
1143}
1144
1145void VideoDialog::scanFinished(bool dbChanged)
1146{
1147 delete m_d->m_scanner;
1148 m_d->m_scanner = nullptr;
1149
1150 if (dbChanged)
1152
1153 m_d->m_currentNode = nullptr;
1154 reloadData();
1155
1156 if (m_d->m_autoMeta)
1158
1159 if (m_d->m_rootNode->childCount() == 1 &&
1161 {
1162 QString message = tr("The video scan found no files, have you "
1163 "configured a video storage group?");
1164 ShowOkPopup(message);
1165 }
1166}
1167
1173{
1174 m_d->m_treeLoaded = false;
1175 refreshData();
1176}
1177
1183{
1184 if (m_d->m_type == DLG_TREE)
1185 {
1187
1188 if (m_d->m_firstLoadPass)
1189 {
1190 m_d->m_firstLoadPass = false;
1191
1193 {
1194 QStringList route =
1195 gCoreContext->GetSetting("mythvideo.VideoTreeLastActive",
1196 "").split("\n");
1198 }
1199 }
1200 }
1201 else
1202 {
1204
1205 if (!m_d->m_treeLoaded)
1206 return;
1207
1208 if (!m_d->m_currentNode)
1209 {
1211 return;
1212 }
1213
1215
1216 // restore the last saved position in the video tree if this is the first
1217 // time this method is called and the option is set in the database
1218 if (m_d->m_firstLoadPass)
1219 {
1221 {
1222 QStringList lastTreeNodePath = gCoreContext->GetSetting("mythvideo.VideoTreeLastActive", "").split("\n");
1223
1224 if (m_d->m_type == DLG_GALLERY || m_d->m_type == DLG_BROWSER)
1225 {
1226 if (!lastTreeNodePath.isEmpty())
1227 {
1228 // go through the path list and set the current node
1229 for (int i = 0; i < lastTreeNodePath.size(); i++)
1230 {
1231 MythGenericTree *node =
1232 m_d->m_currentNode->getChildByName(lastTreeNodePath.at(i));
1233 if (node != nullptr)
1234 {
1235 // check if the node name is the same as the currently selected
1236 // one in the saved tree list. if yes then we are on the right
1237 // way down the video tree to find the last saved position
1238 if (node->GetText().compare(lastTreeNodePath.at(i)) == 0)
1239 {
1240 // set the folder as the new node so we can travel further down
1241 // dont do this if its the last part of the saved video path tree
1242 if (node->getInt() == kSubFolder &&
1243 node->childCount() > 1 &&
1244 i < lastTreeNodePath.size()-1)
1245 {
1246 SetCurrentNode(node);
1247 }
1248 // in the last run the selectedNode will be the last
1249 // entry of the saved tree node.
1250 if (lastTreeNodePath.at(i) == lastTreeNodePath.last())
1251 selectedNode = node;
1252 }
1253 }
1254 }
1255 m_d->m_firstLoadPass = false;
1256 }
1257 }
1258 }
1259 }
1260
1261 using MGTreeChildList = QList<MythGenericTree *>;
1262 MGTreeChildList *lchildren = m_d->m_currentNode->getAllChildren();
1263
1264 for (auto * child : std::as_const(*lchildren))
1265 {
1266 if (child != nullptr)
1267 {
1268 auto *item =
1269 new MythUIButtonListItem(m_videoButtonList, QString(), nullptr,
1271
1272 item->SetData(QVariant::fromValue(child));
1273
1274 UpdateItem(item);
1275
1276 if (child == selectedNode)
1278 }
1279 }
1280 }
1281
1283}
1284
1290{
1291 if (!item)
1292 return;
1293
1295
1296 VideoMetadata *metadata = GetMetadata(item);
1297
1298 if (metadata)
1299 {
1300 InfoMap metadataMap;
1301 metadata->toMap(metadataMap);
1302 item->SetTextFromMap(metadataMap);
1303 }
1304
1305 MythUIButtonListItemCopyDest dest(item);
1306 CopyMetadataToUI(metadata, dest);
1307 CopyPlaybackStateToUI(m_d->m_playbackState, metadata, item, nullptr);
1308
1309 MythGenericTree *parent = node->getParent();
1310
1311 if (parent && metadata && ((QString::compare(parent->GetText(),
1312 metadata->GetTitle(), Qt::CaseInsensitive) == 0) ||
1313 parent->GetText().startsWith(tr("Season"), Qt::CaseInsensitive)))
1314 item->SetText(metadata->GetSubtitle());
1315 else if (metadata && !metadata->GetSubtitle().isEmpty())
1316 item->SetText(QString("%1: %2").arg(metadata->GetTitle(), metadata->GetSubtitle()));
1317 else
1318 item->SetText(metadata ? metadata->GetTitle() : node->GetText());
1319
1320 QString coverimage = GetCoverImage(node);
1321 QString screenshot = GetScreenshot(node);
1322 QString banner = GetBanner(node);
1323 QString fanart = GetFanart(node);
1324
1325 if (!screenshot.isEmpty() && parent && metadata &&
1326 ((QString::compare(parent->GetText(),
1327 metadata->GetTitle(), Qt::CaseInsensitive) == 0) ||
1328 parent->GetText().startsWith(tr("Season"), Qt::CaseInsensitive)))
1329 {
1330 item->SetImage(screenshot);
1331 }
1332 else
1333 {
1334 if (coverimage.isEmpty())
1335 coverimage = GetFirstImage(node, "Coverart");
1336 item->SetImage(coverimage);
1337 }
1338
1339 int nodeInt = node->getInt();
1340
1341 if (coverimage.isEmpty() && nodeInt == kSubFolder)
1342 coverimage = GetFirstImage(node, "Coverart");
1343
1344 item->SetImage(coverimage, "coverart");
1345
1346 if (screenshot.isEmpty() && nodeInt == kSubFolder)
1347 screenshot = GetFirstImage(node, "Screenshots");
1348
1349 item->SetImage(screenshot, "screenshot");
1350
1351 if (banner.isEmpty() && nodeInt == kSubFolder)
1352 banner = GetFirstImage(node, "Banners");
1353
1354 item->SetImage(banner, "banner");
1355
1356 if (fanart.isEmpty() && nodeInt == kSubFolder)
1357 fanart = GetFirstImage(node, "Fanart");
1358
1359 item->SetImage(fanart, "fanart");
1360
1361 if (nodeInt == kSubFolder)
1362 {
1363 item->SetText(QString("%1").arg(node->visibleChildCount()), "childcount");
1364 item->DisplayState("subfolder", "nodetype");
1365 item->SetText(node->GetText(), "title");
1366 item->SetText(node->GetText());
1367 }
1368 else if (nodeInt == kUpFolder)
1369 {
1370 item->DisplayState("upfolder", "nodetype");
1371 item->SetText(node->GetText(), "title");
1372 item->SetText(node->GetText());
1373 }
1374
1375 if (item == GetItemCurrent())
1376 UpdateText(item);
1377}
1378
1384{
1386 MythGenericTree *oldroot = m_d->m_rootNode;
1387 if (!m_d->m_treeLoaded)
1388 {
1391 m_d->m_parentalLevel.GetLevel(), true);
1392 }
1393 else
1394 {
1395 if (m_d->m_videoList)
1396 {
1398 m_d->m_parentalLevel.GetLevel(),
1400 }
1401 if(m_d->m_videoList)
1403 }
1404
1405 m_d->m_treeLoaded = true;
1406
1407 // Move a node down if there is a single directory item here...
1408 if (m_d->m_rootNode->childCount() == 1)
1409 {
1411 if (node->getInt() == kSubFolder && node->childCount() > 1)
1412 m_d->m_rootNode = node;
1413 else if (node->getInt() == kUpFolder)
1414 m_d->m_treeLoaded = false;
1415 }
1416 else if (m_d->m_rootNode->childCount() == 0)
1417 {
1418 m_d->m_treeLoaded = false;
1419 }
1420
1421 if (!m_d->m_currentNode || m_d->m_rootNode != oldroot)
1423}
1424
1429QString VideoDialog::RemoteImageCheck(const QString& host, const QString& filename)
1430{
1431 QString result = "";
1432#if 0
1433 LOG(VB_GENERAL, LOG_DEBUG, QString("RemoteImageCheck(%1)").arg(filename));
1434#endif
1435
1436 QStringList dirs = GetVideoDirsByHost(host);
1437
1438 if (!dirs.isEmpty())
1439 {
1440 for (const auto & dir : std::as_const(dirs))
1441 {
1442 // These are in the form: myth://Videos@<backend>/path/to/dir
1443 QUrl sgurl { dir };
1444 QString path = sgurl.path();
1445
1446 QString fname = QString("%1/%2").arg(path, filename);
1447
1448 QStringList list( QString("QUERY_SG_FILEQUERY") );
1449 list << host;
1450 list << "Videos";
1451 list << fname;
1452
1453 bool ok = gCoreContext->SendReceiveStringList(list);
1454
1455 if (!ok || list.at(0).startsWith("SLAVE UNREACHABLE"))
1456 {
1457 LOG(VB_GENERAL, LOG_WARNING,
1458 QString("Backend : %1 currently Unreachable. Skipping "
1459 "this one.") .arg(host));
1460 break;
1461 }
1462
1463 if ((!list.isEmpty()) && (list.at(0) == fname))
1464 result = StorageGroup::generate_file_url("Videos", host, filename);
1465
1466 if (!result.isEmpty())
1467 {
1468#if 0
1469 LOG(VB_GENERAL, LOG_DEBUG,
1470 QString("RemoteImageCheck(%1) res :%2: :%3:")
1471 .arg(fname).arg(result).arg(dir));
1472#endif
1473 break;
1474 }
1475
1476 }
1477 }
1478
1479 return result;
1480}
1481
1487{
1488 if (!node)
1489 return {};
1490
1491 int nodeInt = node->getInt();
1492
1493 QString icon_file;
1494
1495 if (nodeInt == kSubFolder) // subdirectory
1496 {
1497 // First validate that the data can be converted
1498 if (!node->GetData().canConvert<TreeNodeData>())
1499 return icon_file;
1500
1501 // load folder icon
1502 QString folder_path = node->GetData().value<TreeNodeData>().GetPath();
1503 QString host = node->GetData().value<TreeNodeData>().GetHost();
1504 QString prefix = node->GetData().value<TreeNodeData>().GetPrefix();
1505
1506 if (folder_path.startsWith("myth://"))
1507 folder_path = folder_path.right(folder_path.length()
1508 - folder_path.lastIndexOf("//") - 1);
1509
1510 QString filename = QString("%1/folder").arg(folder_path);
1511
1512#if 0
1513 LOG(VB_GENERAL, LOG_DEBUG,
1514 QString("GetCoverImage host : %1 prefix : %2 file : %3")
1515 .arg(host).arg(prefix).arg(filename));
1516#endif
1517
1518 QStringList test_files;
1519 test_files.append(filename + ".png");
1520 test_files.append(filename + ".jpg");
1521 test_files.append(filename + ".jpeg");
1522 test_files.append(filename + ".gif");
1523
1524 // coverity[auto_causes_copy]
1525 for (auto imagePath : std::as_const(test_files))
1526 {
1527#if 0
1528 LOG(VB_GENERAL, LOG_DEBUG, QString("Cover check :%1 : ").arg(imagePath));
1529#endif
1530
1531 bool foundCover = false;
1532 if (!host.isEmpty())
1533 {
1534 // Strip out any extra /'s
1535 imagePath.replace("//", "/");
1536 prefix.replace("//","/");
1537 imagePath = imagePath.right(imagePath.length() - (prefix.length() + 1));
1538 QString tmpCover = RemoteImageCheck(host, imagePath);
1539
1540 if (!tmpCover.isEmpty())
1541 {
1542 foundCover = true;
1543 imagePath = tmpCover;
1544 }
1545 }
1546 else
1547 {
1548 foundCover = QFile::exists(imagePath);
1549 }
1550
1551 if (foundCover)
1552 {
1553 icon_file = imagePath;
1554 break;
1555 }
1556 }
1557
1558 // If we found nothing, load the first image we find
1559 if (icon_file.isEmpty())
1560 {
1561 QStringList imageTypes { "*.png", "*.jpg", "*.jpeg", "*.gif" };
1562 QStringList fList;
1563
1564 if (!host.isEmpty())
1565 {
1566 // TODO: This can likely get a little cleaner
1567
1568 QStringList dirs = GetVideoDirsByHost(host);
1569
1570 if (!dirs.isEmpty())
1571 {
1572 for (const auto & dir : std::as_const(dirs))
1573 {
1574 // These are in the form: myth://Videos@<backend>/path/to/dir
1575 QUrl sgurl { dir };
1576 QString path = sgurl.path();
1577
1578 QString subdir = folder_path.right(folder_path.length() - (prefix.length() + 1));
1579
1580 path = path + "/" + subdir;
1581
1582 QStringList tmpList;
1583 bool ok = StorageGroup::remoteGetFileList(host, path, &tmpList, "Videos");
1584
1585 if (ok)
1586 {
1587 for (const auto & pattern : std::as_const(imageTypes))
1588 {
1589 auto rePattern = QRegularExpression::wildcardToRegularExpression(pattern);
1590 QRegularExpression rx {
1591 rePattern.mid(2,rePattern.size()-4), // Remove anchors
1592 QRegularExpression::CaseInsensitiveOption };
1593 QStringList matches = tmpList.filter(rx);
1594 if (!matches.isEmpty())
1595 {
1596 fList.clear();
1597 fList.append(subdir + "/" + matches.at(0).split("::").at(1));
1598 break;
1599 }
1600 }
1601
1602 break;
1603 }
1604 }
1605 }
1606
1607 }
1608 else
1609 {
1610 QDir vidDir(folder_path);
1611 vidDir.setNameFilters(imageTypes);
1612 fList = vidDir.entryList();
1613 }
1614
1615 // Take the Coverfile for the first valid node in the dir, if it exists.
1616 if (icon_file.isEmpty())
1617 {
1618 int list_count = node->visibleChildCount();
1619 if (list_count > 0)
1620 {
1621 for (int i = 0; i < list_count; i++)
1622 {
1623 MythGenericTree *subnode = node->getVisibleChildAt(i);
1624 if (subnode)
1625 {
1626 VideoMetadata *metadata = GetMetadataPtrFromNode(subnode);
1627 if (metadata)
1628 {
1629 if (!metadata->GetHost().isEmpty() &&
1630 !metadata->GetCoverFile().startsWith("/"))
1631 {
1632 QString test_file = StorageGroup::generate_file_url("Coverart",
1633 metadata->GetHost(), metadata->GetCoverFile());
1634 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1635 !IsDefaultCoverFile(test_file))
1636 {
1637 icon_file = test_file;
1638 break;
1639 }
1640 }
1641 else
1642 {
1643 const QString& test_file = metadata->GetCoverFile();
1644 if (!test_file.isEmpty() &&
1645 !IsDefaultCoverFile(test_file))
1646 {
1647 icon_file = test_file;
1648 break;
1649 }
1650 }
1651 }
1652 }
1653 }
1654 }
1655 }
1656
1657 if (!fList.isEmpty())
1658 {
1659 if (host.isEmpty())
1660 {
1661 icon_file = QString("%1/%2").arg(folder_path, fList.at(0));
1662 }
1663 else
1664 {
1665 icon_file = StorageGroup::generate_file_url("Videos", host, fList.at(0));
1666 }
1667 }
1668 }
1669
1670 if (!icon_file.isEmpty())
1671 {
1672 LOG(VB_GENERAL, LOG_DEBUG, QString("Found Image : %1 :")
1673 .arg(icon_file));
1674 }
1675 else
1676 {
1677 LOG(VB_GENERAL, LOG_DEBUG,
1678 QString("Could not find folder cover Image : %1 ")
1679 .arg(folder_path));
1680 }
1681 }
1682 else
1683 {
1684 const VideoMetadata *metadata = GetMetadataPtrFromNode(node);
1685
1686 if (metadata)
1687 {
1688 if (metadata->IsHostSet() &&
1689 !metadata->GetCoverFile().startsWith("/") &&
1690 !IsDefaultCoverFile(metadata->GetCoverFile()))
1691 {
1692 icon_file = StorageGroup::generate_file_url("Coverart", metadata->GetHost(),
1693 metadata->GetCoverFile());
1694 }
1695 else
1696 {
1697 icon_file = metadata->GetCoverFile();
1698 }
1699 }
1700 }
1701
1702 if (IsDefaultCoverFile(icon_file))
1703 icon_file.clear();
1704
1705 return icon_file;
1706}
1707
1720 const QString& gpnode, int levels)
1721{
1722 if (!node || type.isEmpty())
1723 return {};
1724
1725 QString icon_file;
1726
1727 int list_count = node->visibleChildCount();
1728 if (list_count > 0)
1729 {
1730 QList<MythGenericTree *> subDirs;
1731 static constexpr int maxRecurse { 1 };
1732
1733 for (int i = 0; i < list_count; i++)
1734 {
1735 MythGenericTree *subnode = node->getVisibleChildAt(i);
1736 if (subnode)
1737 {
1738 if (subnode->childCount() > 0)
1739 subDirs << subnode;
1740
1741 VideoMetadata *metadata = GetMetadataPtrFromNode(subnode);
1742 if (metadata)
1743 {
1744 QString test_file;
1745 const QString& host = metadata->GetHost();
1746 const QString& title = metadata->GetTitle();
1747
1748 if (type == "Coverart" && !host.isEmpty() &&
1749 !metadata->GetCoverFile().startsWith("/"))
1750 {
1751 test_file = StorageGroup::generate_file_url("Coverart",
1752 host, metadata->GetCoverFile());
1753 }
1754 else if (type == "Coverart")
1755 {
1756 test_file = metadata->GetCoverFile();
1757 }
1758
1759 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1760 !IsDefaultCoverFile(test_file) && (gpnode.isEmpty() ||
1761 (QString::compare(gpnode, title, Qt::CaseInsensitive) == 0)))
1762 {
1763 icon_file = test_file;
1764 break;
1765 }
1766
1767 if (type == "Fanart" && !host.isEmpty() &&
1768 !metadata->GetFanart().startsWith("/"))
1769 {
1770 test_file = StorageGroup::generate_file_url("Fanart",
1771 host, metadata->GetFanart());
1772 }
1773 else if (type == "Fanart")
1774 {
1775 test_file = metadata->GetFanart();
1776 }
1777
1778 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1779 test_file != VIDEO_FANART_DEFAULT && (gpnode.isEmpty() ||
1780 (QString::compare(gpnode, title, Qt::CaseInsensitive) == 0)))
1781 {
1782 icon_file = test_file;
1783 break;
1784 }
1785
1786 if (type == "Banners" && !host.isEmpty() &&
1787 !metadata->GetBanner().startsWith("/"))
1788 {
1789 test_file = StorageGroup::generate_file_url("Banners",
1790 host, metadata->GetBanner());
1791 }
1792 else if (type == "Banners")
1793 {
1794 test_file = metadata->GetBanner();
1795 }
1796
1797 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1798 test_file != VIDEO_BANNER_DEFAULT && (gpnode.isEmpty() ||
1799 (QString::compare(gpnode, title, Qt::CaseInsensitive) == 0)))
1800 {
1801 icon_file = test_file;
1802 break;
1803 }
1804
1805 if (type == "Screenshots" && !host.isEmpty() &&
1806 !metadata->GetScreenshot().startsWith("/"))
1807 {
1808 test_file = StorageGroup::generate_file_url("Screenshots",
1809 host, metadata->GetScreenshot());
1810 }
1811 else if (type == "Screenshots")
1812 {
1813 test_file = metadata->GetScreenshot();
1814 }
1815
1816 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1817 test_file != VIDEO_SCREENSHOT_DEFAULT && (gpnode.isEmpty() ||
1818 (QString::compare(gpnode, title, Qt::CaseInsensitive) == 0)))
1819 {
1820 icon_file = test_file;
1821 break;
1822 }
1823 }
1824 }
1825 }
1826 if (icon_file.isEmpty() && !subDirs.isEmpty())
1827 {
1828 QString test_file;
1829 int subDirCount = subDirs.count();
1830 for (int i = 0; i < subDirCount; i ++)
1831 {
1832 if (levels < maxRecurse)
1833 {
1834 test_file = GetFirstImage(subDirs[i], type,
1835 node->GetText(), levels + 1);
1836 if (!test_file.isEmpty())
1837 {
1838 icon_file = test_file;
1839 break;
1840 }
1841 }
1842 }
1843 }
1844 }
1845 return icon_file;
1846}
1847
1853{
1854 const int nodeInt = node->getInt();
1855
1856 QString icon_file;
1857
1858 if (nodeInt == kSubFolder || nodeInt == kUpFolder) // subdirectory
1859 {
1860 icon_file = VIDEO_SCREENSHOT_DEFAULT;
1861 }
1862 else
1863 {
1864 const VideoMetadata *metadata = GetMetadataPtrFromNode(node);
1865
1866 if (metadata)
1867 {
1868 if (metadata->IsHostSet() &&
1869 !metadata->GetScreenshot().startsWith("/") &&
1870 !metadata->GetScreenshot().isEmpty())
1871 {
1872 icon_file = StorageGroup::generate_file_url("Screenshots", metadata->GetHost(),
1873 metadata->GetScreenshot());
1874 }
1875 else
1876 {
1877 icon_file = metadata->GetScreenshot();
1878 }
1879 }
1880 }
1881
1882 if (IsDefaultScreenshot(icon_file))
1883 icon_file.clear();
1884
1885 return icon_file;
1886}
1887
1893{
1894 const int nodeInt = node->getInt();
1895
1896 if (nodeInt == kSubFolder || nodeInt == kUpFolder)
1897 return {};
1898
1899 QString icon_file;
1900 const VideoMetadata *metadata = GetMetadataPtrFromNode(node);
1901
1902 if (metadata)
1903 {
1904 if (metadata->IsHostSet() &&
1905 !metadata->GetBanner().startsWith("/") &&
1906 !metadata->GetBanner().isEmpty())
1907 {
1908 icon_file = StorageGroup::generate_file_url("Banners", metadata->GetHost(),
1909 metadata->GetBanner());
1910 }
1911 else
1912 {
1913 icon_file = metadata->GetBanner();
1914 }
1915
1916 if (IsDefaultBanner(icon_file))
1917 icon_file.clear();
1918 }
1919
1920 return icon_file;
1921}
1922
1928{
1929 const int nodeInt = node->getInt();
1930
1931 if (nodeInt == kSubFolder || nodeInt == kUpFolder) // subdirectory
1932 return {};
1933
1934 QString icon_file;
1935 const VideoMetadata *metadata = GetMetadataPtrFromNode(node);
1936
1937 if (metadata)
1938 {
1939 if (metadata->IsHostSet() &&
1940 !metadata->GetFanart().startsWith("/") &&
1941 !metadata->GetFanart().isEmpty())
1942 {
1943 icon_file = StorageGroup::generate_file_url("Fanart", metadata->GetHost(),
1944 metadata->GetFanart());
1945 }
1946 else
1947 {
1948 icon_file = metadata->GetFanart();
1949 }
1950
1951 if (IsDefaultFanart(icon_file))
1952 icon_file.clear();
1953 }
1954
1955 return icon_file;
1956}
1957
1962bool VideoDialog::keyPressEvent(QKeyEvent *levent)
1963{
1964 if (GetFocusWidget()->keyPressEvent(levent))
1965 return true;
1966
1967 QStringList actions;
1968 bool handled = GetMythMainWindow()->TranslateKeyPress("Video", levent, actions);
1969
1970 for (int i = 0; i < actions.size() && !handled; i++)
1971 {
1972 const QString& action = actions[i];
1973 handled = true;
1974
1975 if (action == "INFO")
1976 {
1979 if (!m_menuPopup && node->getInt() != kUpFolder)
1980 VideoMenu();
1981 }
1982 else if (action == "INCPARENT")
1983 {
1984 shiftParental(1);
1985 }
1986 else if (action == "DECPARENT")
1987 {
1988 shiftParental(-1);
1989 }
1990 else if (action == "1" || action == "2" ||
1991 action == "3" || action == "4")
1992 {
1994 }
1995 else if (action == "FILTER")
1996 {
1997 ChangeFilter();
1998 }
1999 else if (action == "MENU")
2000 {
2001 if (!m_menuPopup)
2002 DisplayMenu();
2003 }
2004 else if (action == "PLAYALT")
2005 {
2008 playVideoAlt();
2009 }
2010 else if (action == "DOWNLOADDATA")
2011 {
2013 VideoSearch();
2014 }
2015 else if (action == "INCSEARCH")
2016 {
2017 searchStart();
2018 }
2019 else if (action == "ITEMDETAIL")
2020 {
2022 }
2023 else if (action == "DELETE")
2024 {
2026 RemoveVideo();
2027 }
2028 else if (action == "EDIT" && !m_menuPopup)
2029 {
2030 EditMetadata();
2031 }
2032 else if (action == "ESCAPE")
2033 {
2034 if (m_d->m_type != DLG_TREE
2035 && !GetMythMainWindow()->IsExitingToMain()
2037 handled = goBack();
2038 else
2039 handled = false;
2040 }
2041 else
2042 {
2043 handled = false;
2044 }
2045 }
2046
2047 if (!handled)
2048 {
2049 handled = GetMythMainWindow()->TranslateKeyPress("TV Frontend", levent,
2050 actions);
2051
2052 for (int i = 0; i < actions.size() && !handled; i++)
2053 {
2054 const QString& action = actions[i];
2055 if (action == "PLAYBACK")
2056 {
2057 handled = true;
2058 playVideo();
2059 }
2060 }
2061 }
2062
2063 if (!handled && MythScreenType::keyPressEvent(levent))
2064 handled = true;
2065
2066 return handled;
2067}
2068
2073void VideoDialog::createBusyDialog(const QString &title)
2074{
2075 if (m_busyPopup)
2076 return;
2077
2078 const QString& message = title;
2079
2081 "mythvideobusydialog");
2082
2083 if (m_busyPopup->Create())
2085}
2086
2092{
2093 if (m_d->m_notifications.contains(metadata->GetHash()))
2094 return;
2095
2096 int id = GetNotificationCenter()->Register(this);
2097 m_d->m_notifications[metadata->GetHash()] = id;
2098
2099 QString msg = tr("Fetching details for %1")
2100 .arg(metadata->GetTitle());
2101 QString desc;
2102 if (metadata->GetSeason() > 0 || metadata->GetEpisode() > 0)
2103 {
2104 desc = tr("Season %1, Episode %2")
2105 .arg(metadata->GetSeason()).arg(metadata->GetEpisode());
2106 }
2107 MythBusyNotification n(msg, sLocation, desc);
2108 n.SetId(id);
2109 n.SetParent(this);
2111}
2112
2114{
2115 if (!metadata || !m_d->m_notifications.contains(metadata->GetHash()))
2116 return;
2117
2118 int id = m_d->m_notifications[metadata->GetHash()];
2119 m_d->m_notifications.remove(metadata->GetHash());
2120
2121 QString msg;
2122 if (ok)
2123 {
2124 msg = tr("Retrieved details for %1").arg(metadata->GetTitle());
2125 }
2126 else
2127 {
2128 msg = tr("Failed to retrieve details for %1").arg(metadata->GetTitle());
2129 }
2130 QString desc;
2131 if (metadata->GetSeason() > 0 || metadata->GetEpisode() > 0)
2132 {
2133 desc = tr("Season %1, Episode %2")
2134 .arg(metadata->GetSeason()).arg(metadata->GetEpisode());
2135 }
2136 if (ok)
2137 {
2138 MythCheckNotification n(msg, sLocation, desc);
2139 n.SetId(id);
2140 n.SetParent(this);
2142 }
2143 else
2144 {
2145 MythErrorNotification n(msg, sLocation, desc);
2146 n.SetId(id);
2147 n.SetParent(this);
2149 }
2150 GetNotificationCenter()->UnRegister(this, id);
2151}
2152
2157void VideoDialog::createOkDialog(const QString& title)
2158{
2159 const QString& message = title;
2160
2161 auto *okPopup = new MythConfirmationDialog(m_popupStack, message, false);
2162
2163 if (okPopup->Create())
2164 m_popupStack->AddScreen(okPopup);
2165}
2166
2171void VideoDialog::searchComplete(const QString& string)
2172{
2173 LOG(VB_GENERAL, LOG_DEBUG, QString("Jumping to: %1").arg(string));
2174
2176 QList<MythGenericTree*> *children = nullptr;
2177 QMap<int, QString> idTitle;
2178
2179 if (parent && m_d->m_type == DLG_TREE)
2180 children = parent->getAllChildren();
2181 else
2182 children = m_d->m_currentNode->getAllChildren();
2183
2184 for (auto * child : std::as_const(*children))
2185 {
2186 QString title = child->GetText();
2187 int id = child->getPosition();
2188 idTitle.insert(id, title);
2189 }
2190
2191 if (m_d->m_type == DLG_TREE)
2192 {
2194 MythGenericTree *new_node = dlgParent->getChildAt(idTitle.key(string));
2195 if (new_node)
2196 {
2199 }
2200 }
2201 else
2202 {
2203 m_videoButtonList->SetItemCurrent(idTitle.key(string));
2204 }
2205}
2206
2212{
2214
2215 QStringList childList;
2216 QList<MythGenericTree*> *children = nullptr;
2217 if (parent && m_d->m_type == DLG_TREE)
2218 children = parent->getAllChildren();
2219 else
2220 children = m_d->m_currentNode->getAllChildren();
2221
2222 for (auto * child : std::as_const(*children))
2223 {
2224 childList << child->GetText();
2225 }
2226
2227 MythScreenStack *popupStack =
2228 GetMythMainWindow()->GetStack("popup stack");
2229 auto *searchDialog = new MythUISearchDialog(popupStack,
2230 tr("Video Search"), childList, false, "");
2231
2232 if (searchDialog->Create())
2233 {
2234 connect(searchDialog, &MythUISearchDialog::haveResult,
2236
2237 popupStack->AddScreen(searchDialog);
2238 }
2239 else
2240 {
2241 delete searchDialog;
2242 }
2243}
2244
2250{
2251 bool handled = false;
2252
2254 {
2256 if (lparent)
2257 {
2258 SetCurrentNode(lparent);
2259
2260 handled = true;
2261 }
2262 }
2263
2264 loadData();
2265
2266 return handled;
2267}
2268
2274{
2275 if (!node)
2276 return;
2277
2278 m_d->m_currentNode = node;
2279}
2280
2286{
2288 MythUIButtonList *currentList = ci ? ci->parent() : nullptr;
2289
2290 if (!currentList)
2291 return;
2292
2293 CheckedSet(m_positionText, tr("%1 of %2")
2294 .arg(currentList->IsEmpty() ? 0 : currentList->GetCurrentPos() + 1)
2295 .arg(currentList->GetCount()));
2296}
2297
2303{
2304 if (!item)
2305 return;
2306
2307 VideoMetadata *metadata = GetMetadata(item);
2308 if (!metadata)
2309 return;
2310
2311 CopyPlaybackStateToUI(m_d->m_playbackState, metadata, item, nullptr);
2312}
2313
2319{
2320 if (!item)
2321 return;
2322
2323 MythUIButtonList *currentList = item->parent();
2324
2325 if (!currentList)
2326 return;
2327
2328 VideoMetadata *metadata = GetMetadata(item);
2329
2331
2332 if (!node)
2333 return;
2334
2335 if (metadata)
2336 {
2337 InfoMap metadataMap;
2338 metadata->toMap(metadataMap);
2339 SetTextFromMap(metadataMap);
2340 }
2341 else
2342 {
2343 InfoMap metadataMap;
2344 ClearMap(metadataMap);
2345 SetTextFromMap(metadataMap);
2346 }
2347
2348 ScreenCopyDest dest(this);
2349 CopyMetadataToUI(metadata, dest);
2350 CopyPlaybackStateToUI(m_d->m_playbackState, metadata, item, m_d->m_currentNode ? this : nullptr);
2351
2352 if (node->getInt() == kSubFolder && !metadata)
2353 {
2354 QString cover = GetFirstImage(node, "Coverart");
2355 QString fanart = GetFirstImage(node, "Fanart");
2356 QString banner = GetFirstImage(node, "Banners");
2357 QString screenshot = GetFirstImage(node, "Screenshots");
2358 CheckedSet(m_coverImage, cover);
2359 CheckedSet(m_fanart, fanart);
2360 CheckedSet(m_banner, banner);
2361 CheckedSet(m_screenshot, screenshot);
2362 }
2363
2364 if (!metadata)
2365 CheckedSet(m_titleText, item->GetText());
2367
2368 if (m_d->m_currentNode)
2369 {
2371 CheckedSet(this, "foldername", m_d->m_currentNode->GetText());
2372 }
2373
2374 if (node && node->getInt() == kSubFolder)
2375 CheckedSet(this, "childcount",
2376 QString("%1").arg(node->visibleChildCount()));
2377
2378 if (node)
2379 node->becomeSelectedChild();
2380}
2381
2391{
2392 if (!gCoreContext->GetBoolSetting("AutomaticSetWatched", false))
2393 return;
2394
2395 if (!item)
2396 return;
2397
2398 VideoMetadata *metadata = GetMetadata(item);
2399 if (!metadata)
2400 return;
2401
2402 auto metadataNew = VideoMetadataListManager::loadOneFromDatabase(metadata->GetID());
2403 if (metadata->GetWatched() != metadataNew->GetWatched())
2404 {
2405 metadata->SetWatched(metadataNew->GetWatched());
2406 item->DisplayState(WatchedToState(metadata->GetWatched()), "watchedstate");
2407 }
2408}
2409
2415{
2417 QString label;
2418
2419 if (metadata)
2420 {
2421 if (!metadata->GetSubtitle().isEmpty())
2422 {
2423 label = tr("Video Options\n%1\n%2").arg(metadata->GetTitle(),
2424 metadata->GetSubtitle());
2425 }
2426 else
2427 {
2428 label = tr("Video Options\n%1").arg(metadata->GetTitle());
2429 }
2430 }
2431 else
2432 {
2433 label = tr("Video Options");
2434 }
2435
2436 auto *menu = new MythMenu(label, this, "actions");
2437
2440 if (metadata)
2441 {
2442 if (!metadata->GetTrailer().isEmpty() ||
2443 gCoreContext->GetBoolSetting("mythvideo.TrailersRandomEnabled", false) ||
2445 menu->AddItem(tr("Play..."), nullptr, CreatePlayMenu());
2446 else
2447 menu->AddItem(tr("Play"), &VideoDialog::playVideo);
2448 if (metadata->GetWatched())
2449 menu->AddItem(tr("Mark as Unwatched"), &VideoDialog::ToggleWatched);
2450 else
2451 menu->AddItem(tr("Mark as Watched"), &VideoDialog::ToggleWatched);
2452 menu->AddItem(tr("Video Info"), nullptr, CreateInfoMenu());
2453 if (!m_d->m_notifications.contains(metadata->GetHash()))
2454 {
2455 menu->AddItem(tr("Change Video Details"), nullptr, CreateManageMenu());
2456 }
2457 menu->AddItem(tr("Delete"), &VideoDialog::RemoveVideo);
2458 }
2459 else if (node && node->getInt() != kUpFolder)
2460 {
2461 menu->AddItem(tr("Play Folder"), &VideoDialog::playFolder);
2462 }
2463
2464
2465 m_menuPopup = new MythDialogBox(menu, m_popupStack, "videomenupopup");
2466
2467 if (m_menuPopup->Create())
2468 {
2471 }
2472 else
2473 {
2474 delete m_menuPopup;
2475 }
2476}
2477
2484{
2486 QString label;
2487
2488 if (metadata)
2489 label = tr("Playback Options\n%1").arg(metadata->GetTitle());
2490 else
2491 return nullptr;
2492
2493 auto *menu = new MythMenu(label, this, "actions");
2494
2495 menu->AddItem(tr("Play"), &VideoDialog::playVideo);
2496
2498 {
2499 menu->AddItem(tr("Play in Alternate Player"), &VideoDialog::playVideoAlt);
2500 }
2501
2502 if (gCoreContext->GetBoolSetting("mythvideo.TrailersRandomEnabled", false))
2503 {
2504 menu->AddItem(tr("Play With Trailers"), &VideoDialog::playVideoWithTrailers);
2505 }
2506
2507 QString trailerFile = metadata->GetTrailer();
2508 if (QFile::exists(trailerFile) ||
2509 (!metadata->GetHost().isEmpty() && !trailerFile.isEmpty()))
2510 {
2511 menu->AddItem(tr("Play Trailer"), &VideoDialog::playTrailer);
2512 }
2513
2514 return menu;
2515}
2516
2522{
2523 QString label = tr("Video Display Menu");
2524
2525 auto *menu = new MythMenu(label, this, "display");
2526
2527 menu->AddItem(tr("Scan For Changes"), &VideoDialog::doVideoScan);
2528 menu->AddItem(tr("Retrieve All Details"), qOverload<>(&VideoDialog::VideoAutoSearch));
2529 menu->AddItem(tr("Filter Display"), &VideoDialog::ChangeFilter);
2530 menu->AddItem(tr("Browse By..."), nullptr, CreateMetadataBrowseMenu());
2531 menu->AddItem(tr("Change View"), nullptr, CreateViewMenu());
2532 menu->AddItem(tr("Settings"), nullptr, CreateSettingsMenu());
2533
2534 m_menuPopup = new MythDialogBox(menu, m_popupStack, "videomenupopup");
2535
2536 if (m_menuPopup->Create())
2537 {
2540 }
2541 else
2542 {
2543 delete m_menuPopup;
2544 }
2545}
2546
2547// Switch from the display menu to the actions menu on second
2548// menu press
2549
2550void VideoDialog::popupClosed(const QString& which, int result)
2551{
2552 m_menuPopup = nullptr;
2553
2554 if (result == -2)
2555 {
2556 if (which == "display")
2557 VideoMenu();
2558 }
2559}
2560
2566{
2567 QString label = tr("Change View");
2568
2569 auto *menu = new MythMenu(label, this, "view");
2570
2571 if (!(m_d->m_type & DLG_BROWSER))
2572 menu->AddItem(tr("Switch to Browse View"), &VideoDialog::SwitchBrowse);
2573
2574 if (!(m_d->m_type & DLG_GALLERY))
2575 menu->AddItem(tr("Switch to Gallery View"), &VideoDialog::SwitchGallery);
2576
2577 if (!(m_d->m_type & DLG_TREE))
2578 menu->AddItem(tr("Switch to List View"), &VideoDialog::SwitchTree);
2579
2580 if (!(m_d->m_type & DLG_MANAGER))
2581 menu->AddItem(tr("Switch to Manage View"), &VideoDialog::SwitchManager);
2582
2583 if (m_d->m_isFlatList)
2584 menu->AddItem(tr("Show Directory Structure"), &VideoDialog::ToggleFlatView);
2585 else
2586 menu->AddItem(tr("Hide Directory Structure"), &VideoDialog::ToggleFlatView);
2587
2588 if (m_d->m_isFileBrowser)
2589 menu->AddItem(tr("Browse Library (recommended)"), &VideoDialog::ToggleBrowseMode);
2590 else
2591 menu->AddItem(tr("Browse Filesystem (slow)"), &VideoDialog::ToggleBrowseMode);
2592
2593
2594 return menu;
2595}
2596
2602{
2603 QString label = tr("Video Settings");
2604
2605 auto *menu = new MythMenu(label, this, "settings");
2606
2607 menu->AddItem(tr("Player Settings"), &VideoDialog::ShowPlayerSettings);
2608 menu->AddItem(tr("Metadata Settings"), &VideoDialog::ShowMetadataSettings);
2609 menu->AddItem(tr("File Type Settings"), &VideoDialog::ShowExtensionSettings);
2610
2611 return menu;
2612}
2613
2619{
2620 auto *ps = new PlayerSettings(m_mainStack, "player settings");
2621
2622 if (ps->Create())
2624 else
2625 delete ps;
2626}
2627
2633{
2634 auto *ms = new MetadataSettings(m_mainStack, "metadata settings");
2635
2636 if (ms->Create())
2638 else
2639 delete ms;
2640}
2641
2647{
2648 auto *fa = new FileAssocDialog(m_mainStack, "fa dialog");
2649
2650 if (fa->Create())
2652 else
2653 delete fa;
2654}
2655
2661{
2662 QString label = tr("Browse By");
2663
2664 auto *menu = new MythMenu(label, this, "metadata");
2665
2666 if (m_d->m_groupType != BRS_CAST)
2667 menu->AddItem(tr("Cast"), &VideoDialog::SwitchVideoCastGroup);
2668
2670 menu->AddItem(tr("Category"), &VideoDialog::SwitchVideoCategoryGroup);
2671
2673 menu->AddItem(tr("Date Added"), &VideoDialog::SwitchVideoInsertDateGroup);
2674
2676 menu->AddItem(tr("Director"), &VideoDialog::SwitchVideoDirectorGroup);
2677
2678 if (m_d->m_groupType != BRS_STUDIO)
2679 menu->AddItem(tr("Studio"), &VideoDialog::SwitchVideoStudioGroup);
2680
2681 if (m_d->m_groupType != BRS_FOLDER)
2682 menu->AddItem(tr("Folder"), &VideoDialog::SwitchVideoFolderGroup);
2683
2684 if (m_d->m_groupType != BRS_GENRE)
2685 menu->AddItem(tr("Genre"), &VideoDialog::SwitchVideoGenreGroup);
2686
2687 if (m_d->m_groupType != BRS_TVMOVIE)
2688 menu->AddItem(tr("TV/Movies"), &VideoDialog::SwitchVideoTVMovieGroup);
2689
2691 menu->AddItem(tr("User Rating"), &VideoDialog::SwitchVideoUserRatingGroup);
2692
2693 if (m_d->m_groupType != BRS_YEAR)
2694 menu->AddItem(tr("Year"), &VideoDialog::SwitchVideoYearGroup);
2695
2696 return menu;
2697}
2698
2704{
2705 QString label = tr("Video Info");
2706
2707 auto *menu = new MythMenu(label, this, "info");
2708
2710 menu->AddItem(tr("View Details"), &VideoDialog::DoItemDetailShow2);
2711
2712 menu->AddItem(tr("View Full Plot"), &VideoDialog::ViewPlot);
2713
2715 if (metadata)
2716 {
2717 if (!metadata->GetCast().empty())
2718 menu->AddItem(tr("View Cast"), &VideoDialog::ShowCastDialog);
2719 if (!metadata->GetHomepage().isEmpty())
2720 menu->AddItem(tr("View Homepage"), &VideoDialog::ShowHomepage);
2721 }
2722
2723 return menu;
2724}
2725
2731{
2732 QString label = tr("Manage Video Details");
2733
2734 auto *menu = new MythMenu(label, this, "manage");
2735
2737
2738 menu->AddItem(tr("Edit Details"), &VideoDialog::EditMetadata);
2739 menu->AddItem(tr("Retrieve Details"), qOverload<>(&VideoDialog::VideoSearch));
2740 if (metadata->GetProcessed())
2741 menu->AddItem(tr("Allow Updates"), &VideoDialog::ToggleProcess);
2742 else
2743 menu->AddItem(tr("Disable Updates"), &VideoDialog::ToggleProcess);
2744 menu->AddItem(tr("Reset Details"), &VideoDialog::ResetMetadata);
2745
2746 return menu;
2747}
2748
2750{
2752 if (metadata)
2753 {
2754 metadata->SetProcessed(!metadata->GetProcessed());
2755 metadata->UpdateDatabase();
2756
2757 refreshData();
2758 }
2759}
2760
2766{
2768 gCoreContext->SaveSetting("VideoDialogNoDB",
2769 QString("%1").arg((int)m_d->m_isFileBrowser));
2770 reloadData();
2771}
2772
2778{
2780 gCoreContext->SaveSetting(QString("mythvideo.folder_view_%1").arg(m_d->m_type),
2781 QString("%1").arg((int)m_d->m_isFlatList));
2782 // TODO: This forces a complete tree rebuild, this is SLOW and shouldn't
2783 // be necessary since MythGenericTree can do a flat view without a rebuild,
2784 // I just don't want to re-write VideoList just now
2785 reloadData();
2786}
2787
2793{
2794 SetCurrentNode(node);
2795 loadData();
2796}
2797
2803{
2804 QStringList route = node->getRouteByString();
2805 if (m_d->m_videoList && m_d->m_videoList->refreshNode(node))
2806 reloadData();
2807 if (m_d->m_type == DLG_TREE)
2808 {
2810 }
2811 else
2812 {
2813 }
2814}
2815
2821{
2823 int nodeInt = node->getInt();
2824
2825 switch (nodeInt)
2826 {
2827 case kDynamicSubFolder:
2829 break;
2830 case kSubFolder:
2831 handleDirSelect(node);
2832 break;
2833 case kUpFolder:
2834 goBack();
2835 break;
2836 default:
2837 {
2838 bool doPlay = true;
2839 if (m_d->m_type == DLG_GALLERY)
2840 {
2841 doPlay = !DoItemDetailShow();
2842 }
2843
2844 if (doPlay)
2845 playVideo();
2846 }
2847 };
2848}
2849
2855{
2857}
2858
2864{
2866}
2867
2873{
2875}
2876
2882{
2884}
2885
2891{
2893}
2894
2900{
2902}
2903
2909{
2911}
2912
2918{
2920}
2921
2927{
2929}
2930
2936{
2938}
2939
2945{
2947}
2948
2954{
2956}
2957
2963{
2965}
2966
2972{
2974}
2975
2981{
2982 m_d->m_switchingLayout = true;
2983
2984 // save current position so it can be restored after the switch
2985 SavePosition();
2986
2987 auto *mythvideo =
2988 new VideoDialog(GetMythMainWindow()->GetMainStack(), "mythvideo",
2989 m_d->m_videoList, type, browse);
2990
2991 if (mythvideo->Create())
2992 {
2993 gCoreContext->SaveSetting("Default MythVideo View", type);
2994 gCoreContext->SaveSetting("mythvideo.db_group_type", browse);
2995 MythScreenStack *screenStack = GetScreenStack();
2996 screenStack->AddScreen(mythvideo);
2997 screenStack->PopScreen(this, false, false);
2998 deleteLater();
2999 }
3000 else
3001 {
3002 ShowOkPopup(tr("An error occurred when switching views."));
3003 }
3004}
3005
3011{
3013
3014 auto *plotdialog = new PlotDialog(m_popupStack, metadata);
3015
3016 if (plotdialog->Create())
3017 m_popupStack->AddScreen(plotdialog);
3018}
3019
3025{
3027
3028 if (metadata)
3029 {
3031 auto *idp = new ItemDetailPopup(mainStack, metadata,
3033
3034 if (idp->Create())
3035 {
3036 mainStack->AddScreen(idp);
3037 return true;
3038 }
3039 }
3040
3041 return false;
3042}
3043
3049{
3051
3052 auto *castdialog = new CastDialog(m_popupStack, metadata);
3053
3054 if (castdialog->Create())
3055 m_popupStack->AddScreen(castdialog);
3056}
3057
3059{
3061
3062 if (!metadata)
3063 return;
3064
3065 QString url = metadata->GetHomepage();
3066
3067 if (url.isEmpty())
3068 return;
3069
3070 QString browser = gCoreContext->GetSetting("WebBrowserCommand", "");
3071 QString zoom = gCoreContext->GetSetting("WebBrowserZoomLevel", "1.0");
3072
3073 if (browser.isEmpty())
3074 {
3075 ShowOkPopup(tr("No browser command set! MythVideo needs MythBrowser "
3076 "installed to display the homepage."));
3077 return;
3078 }
3079
3080 if (browser.toLower() == "internal")
3081 {
3082 GetMythMainWindow()->HandleMedia("WebBrowser", url);
3083 return;
3084 }
3085
3086 QString cmd = browser;
3087 cmd.replace("%ZOOM%", zoom);
3088 cmd.replace("%URL%", url);
3089 cmd.replace('\'', "%27");
3090 cmd.replace("&","\\&");
3091 cmd.replace(";","\\;");
3092
3093 GetMythMainWindow()->AllowInput(false);
3096}
3097
3103{
3105 if (metadata && m_d->m_videoList)
3107}
3108
3114{
3116 if (metadata && m_d->m_videoList)
3117 PlayVideo(metadata->GetFilename(), m_d->m_videoList->getListCache(), true);
3118}
3119
3125{
3126 const int WATCHED_WATERMARK = 10000; // Play less then this milisec and the chain of
3127 // videos will not be followed when
3128 // playing.
3131 if (!node || (node->getInt() >= 0))
3132 return;
3133 int list_count = node->childCount();
3134 if (list_count <= 0)
3135 return;
3136
3137 for (int i = 0; i < list_count; i++)
3138 {
3139 MythGenericTree *subnode = node->getChildAt(i);
3140 if (!subnode)
3141 continue;
3142 VideoMetadata *metadata = GetMetadataPtrFromNode(subnode);
3143 if (!metadata || !m_d->m_videoList)
3144 continue;
3145 QElapsedTimer playing_time;
3146 playing_time.start();
3148 if (!playing_time.hasExpired(WATCHED_WATERMARK))
3149 break;
3150 }
3151}
3152
3153namespace
3154{
3156 {
3157 explicit SimpleCollect(QStringList &fileList) : m_fileList(fileList) {}
3158
3159 DirectoryHandler *newDir([[maybe_unused]] const QString &dirName,
3160 [[maybe_unused]] const QString &fqDirName) override // DirectoryHandler
3161 {
3162 return this;
3163 }
3164
3165 void handleFile([[maybe_unused]] const QString &fileName,
3166 const QString &fqFileName,
3167 [[maybe_unused]] const QString &extension,
3168 [[maybe_unused]] const QString &host) override // DirectoryHandler
3169 {
3170 m_fileList.push_back(fqFileName);
3171 }
3172
3173 private:
3174 QStringList &m_fileList;
3175 };
3176
3177 QStringList GetTrailersInDirectory(const QString &startDir)
3178 {
3181 .getExtensionIgnoreList(extensions);
3182 QStringList ret;
3183 SimpleCollect sc(ret);
3184
3185 (void) ScanVideoDirectory(startDir, &sc, extensions, false);
3186 return ret;
3187 }
3188}
3189
3195{
3197 if (!metadata) return;
3198
3200 GetSetting("mythvideo.TrailersDir"));
3201
3202 if (trailers.isEmpty())
3203 return;
3204
3205 const int trailersToPlay =
3206 gCoreContext->GetNumSetting("mythvideo.TrailersRandomCount");
3207
3208 int i = 0;
3209 while (!trailers.isEmpty() && i < trailersToPlay)
3210 {
3211 ++i;
3212 QString trailer = trailers.takeAt(MythRandom(0, trailers.size() - 1));
3213
3214 LOG(VB_GENERAL, LOG_DEBUG,
3215 QString("Random trailer to play will be: %1").arg(trailer));
3216
3218 }
3219
3221}
3222
3228{
3230 if (!metadata) return;
3231 QString url;
3232
3233 if (metadata->IsHostSet() && !metadata->GetTrailer().startsWith("/"))
3234 {
3235 url = StorageGroup::generate_file_url("Trailers", metadata->GetHost(),
3236 metadata->GetTrailer());
3237 }
3238 else
3239 {
3240 url = metadata->GetTrailer();
3241 }
3242
3244}
3245
3251{
3252 m_d->m_parentalLevel.SetLevel(level);
3253}
3254
3260{
3262 .GetLevel() + amount).GetLevel());
3263}
3264
3270{
3271 MythScreenStack *mainStack = GetScreenStack();
3272
3273 auto *filterdialog = new VideoFilterDialog(mainStack,
3274 "videodialogfilters", m_d->m_videoList.get());
3275
3276 if (filterdialog->Create())
3277 mainStack->AddScreen(filterdialog);
3278
3279 connect(filterdialog, &VideoFilterDialog::filterChanged, this, &VideoDialog::reloadData);
3280}
3281
3287{
3288 VideoMetadata *metadata = nullptr;
3289
3290 if (item)
3291 {
3293 if (node)
3294 {
3295 int nodeInt = node->getInt();
3296
3297 if (nodeInt >= 0)
3298 metadata = GetMetadataPtrFromNode(node);
3299 }
3300 }
3301
3302 return metadata;
3303}
3304
3305void VideoDialog::customEvent(QEvent *levent)
3306{
3307 if (levent->type() == MetadataFactoryMultiResult::kEventType)
3308 {
3309 auto *mfmr = dynamic_cast<MetadataFactoryMultiResult*>(levent);
3310
3311 if (!mfmr)
3312 return;
3313
3314 MetadataLookupList list = mfmr->m_results;
3315
3316 if (list.count() > 1)
3317 {
3318 auto *metadata = list[0]->GetData().value<VideoMetadata *>();
3319 dismissFetchDialog(metadata, true);
3320 auto *resultsdialog = new MetadataResultsDialog(m_popupStack, list);
3321
3322 connect(resultsdialog, &MetadataResultsDialog::haveResult,
3324 Qt::QueuedConnection);
3325
3326 if (resultsdialog->Create())
3327 m_popupStack->AddScreen(resultsdialog);
3328 }
3329 }
3330 else if (levent->type() == MetadataFactorySingleResult::kEventType)
3331 {
3332 auto *mfsr = dynamic_cast<MetadataFactorySingleResult*>(levent);
3333
3334 if (!mfsr)
3335 return;
3336
3337 MetadataLookup *lookup = mfsr->m_result;
3338
3339 if (!lookup)
3340 return;
3341
3342 OnVideoSearchDone(lookup);
3343 }
3344 else if (levent->type() == MetadataFactoryNoResult::kEventType)
3345 {
3346 auto *mfnr = dynamic_cast<MetadataFactoryNoResult*>(levent);
3347
3348 if (!mfnr)
3349 return;
3350
3351 MetadataLookup *lookup = mfnr->m_result;
3352
3353 if (!lookup)
3354 return;
3355
3356 auto *metadata = lookup->GetData().value<VideoMetadata *>();
3357 if (metadata)
3358 {
3359 dismissFetchDialog(metadata, false);
3360 metadata->SetProcessed(true);
3361 metadata->UpdateDatabase();
3362 }
3363 LOG(VB_GENERAL, LOG_INFO,
3364 QString("No results found for %1 %2 %3").arg(lookup->GetTitle())
3365 .arg(lookup->GetSeason()).arg(lookup->GetEpisode()));
3366 }
3367 else if (levent->type() == DialogCompletionEvent::kEventType)
3368 {
3369 auto *dce = dynamic_cast<DialogCompletionEvent *>(levent);
3370 if (dce != nullptr)
3371 {
3372 QString id = dce->GetId();
3373
3374 if (id == "scanprompt")
3375 {
3376 int result = dce->GetResult();
3377 if (result == 1)
3378 doVideoScan();
3379 }
3380 else
3381 {
3382 m_menuPopup = nullptr;
3383 }
3384 }
3385 else
3386 {
3387 m_menuPopup = nullptr;
3388 }
3389 }
3390 else if (levent->type() == ImageDLFailureEvent::kEventType)
3391 {
3392 MythErrorNotification n(tr("Failed to retrieve image(s)"),
3393 sLocation,
3394 tr("Check logs"));
3396 }
3397}
3398
3400{
3401 // The metadata has some cover file set
3402 dismissFetchDialog(metadata, true);
3403
3404 metadata->SetProcessed(true);
3405 metadata->UpdateDatabase();
3406
3407 MythUIButtonListItem *item = GetItemByMetadata(metadata);
3408 if (item != nullptr)
3409 UpdateItem(item);
3410}
3411
3413{
3415 {
3417 }
3418
3420}
3421
3423{
3425 {
3427 }
3428
3429 QMap<int, int> idPosition;
3430
3431 QList<MythGenericTree*> *children = m_d->m_currentNode->getAllChildren();
3432
3433 for (auto * child : std::as_const(*children))
3434 {
3435 int nodeInt = child->getInt();
3436 if (nodeInt != kSubFolder && nodeInt != kUpFolder)
3437 {
3438 VideoMetadata *listmeta =
3440 if (listmeta)
3441 {
3442 int position = child->getPosition();
3443 int id = listmeta->GetID();
3444 idPosition.insert(id, position);
3445 }
3446 }
3447 }
3448
3449 return m_videoButtonList->GetItemAt(idPosition.value(metadata->GetID()));
3450}
3451
3453 bool automode)
3454{
3455 if (!node)
3457
3458 if (!node)
3459 return;
3460
3461 VideoMetadata *metadata = GetMetadataPtrFromNode(node);
3462
3463 if (!metadata)
3464 return;
3465
3466 m_metadataFactory->Lookup(metadata, automode, true);
3467
3468 if (!automode)
3469 {
3470 createFetchDialog(metadata);
3471 }
3472}
3473
3475{
3476 if (!node)
3477 node = m_d->m_rootNode;
3478 using MGTreeChildList = QList<MythGenericTree *>;
3479 MGTreeChildList *lchildren = node->getAllChildren();
3480
3481 LOG(VB_GENERAL, LOG_DEBUG,
3482 QString("Fetching details in %1").arg(node->GetText()));
3483
3484 for (auto * child : std::as_const(*lchildren))
3485 {
3486 if ((child->getInt() == kSubFolder) ||
3487 (child->getInt() == kUpFolder))
3488 {
3489 VideoAutoSearch(child);
3490 }
3491 else
3492 {
3493 VideoMetadata *metadata = GetMetadataPtrFromNode(child);
3494
3495 if (!metadata)
3496 continue;
3497
3498 if (!metadata->GetProcessed())
3499 VideoSearch(child, true);
3500 }
3501 }
3502}
3503
3505{
3507 if (!item)
3508 return;
3509
3510 VideoMetadata *metadata = GetMetadata(item);
3511 if (metadata)
3512 {
3513 metadata->SetWatched(!metadata->GetWatched());
3514 metadata->UpdateDatabase();
3515 item->DisplayState(WatchedToState(metadata->GetWatched()),
3516 "watchedstate");
3517 }
3518}
3519
3521{
3522 if (!lookup)
3523 return;
3524
3525 if(!lookup->GetInetref().isEmpty() && lookup->GetInetref() != "00000000")
3526 {
3527 LOG(VB_GENERAL, LOG_INFO, LOC_MML +
3528 QString("Selected Item: Type: %1%2 : Subtype: %3%4%5 : InetRef: %6")
3529 .arg(lookup->GetType() == kMetadataVideo ? "Video" : "",
3530 lookup->GetType() == kMetadataRecording ? "Recording" : "",
3531 lookup->GetSubtype() == kProbableMovie ? "Movie" : "",
3532 lookup->GetSubtype() == kProbableTelevision ? "Television" : "",
3533 lookup->GetSubtype() == kUnknownVideo ? "Unknown" : "",
3534 lookup->GetInetref()));
3535
3536 lookup->SetStep(kLookupData);
3537 lookup->IncrRef();
3538 m_metadataFactory->Lookup(lookup);
3539 }
3540 else
3541 {
3542 LOG(VB_GENERAL, LOG_ERR, LOC_MML +
3543 QString("Selected Item has no InetRef Number!"));
3544
3545 OnVideoSearchDone(lookup);
3546 }
3547}
3548
3550{
3552 if (metadata)
3553 {
3554 ParentalLevel curshowlevel = metadata->GetShowLevel();
3555
3556 curshowlevel += amount;
3557
3558 if (curshowlevel.GetLevel() != metadata->GetShowLevel())
3559 {
3560 metadata->SetShowLevel(curshowlevel.GetLevel());
3561 metadata->UpdateDatabase();
3562 refreshData();
3563 }
3564 }
3565}
3566
3568{
3570 if (!metadata)
3571 return;
3572
3573 MythScreenStack *screenStack = GetScreenStack();
3574
3575 auto *md_editor = new EditMetadataDialog(screenStack,
3576 "mythvideoeditmetadata", metadata,
3578
3579 connect(md_editor, &EditMetadataDialog::Finished, this, &VideoDialog::refreshData);
3580
3581 if (md_editor->Create())
3582 screenStack->AddScreen(md_editor);
3583}
3584
3586{
3588
3589 if (!metadata)
3590 return;
3591
3592 QString message = tr("Are you sure you want to permanently delete:\n%1")
3593 .arg(metadata->GetTitle());
3594
3595 auto *confirmdialog = new MythConfirmationDialog(m_popupStack,message);
3596
3597 if (confirmdialog->Create())
3598 m_popupStack->AddScreen(confirmdialog);
3599
3600 connect(confirmdialog, &MythConfirmationDialog::haveResult,
3602}
3603
3605{
3606 if (!dodelete)
3607 return;
3608
3610 MythGenericTree *gtItem = GetNodePtrFromButton(item);
3611
3612 VideoMetadata *metadata = GetMetadata(item);
3613
3614 if (!metadata)
3615 return;
3616
3617 if (m_d->m_videoList && m_d->m_videoList->Delete(metadata->GetID()))
3618 {
3620 m_videoButtonTree->RemoveItem(item, false); // FIXME Segfault when true
3621 else
3623
3624 MythGenericTree *parent = gtItem->getParent();
3625 parent->deleteNode(gtItem);
3626 }
3627 else
3628 {
3629 QString message = tr("Failed to delete file");
3630
3631 auto *confirmdialog = new MythConfirmationDialog(m_popupStack,message,
3632 false);
3633
3634 if (confirmdialog->Create())
3635 m_popupStack->AddScreen(confirmdialog);
3636 }
3637}
3638
3640{
3642 VideoMetadata *metadata = GetMetadata(item);
3643
3644 if (metadata)
3645 {
3646 metadata->Reset();
3647 metadata->UpdateDatabase();
3648 UpdateItem(item);
3649 }
3650}
3651
3653{
3654 if (!metadata)
3655 return;
3656
3657 QStringList cover_dirs;
3658 cover_dirs += m_d->m_artDir;
3659
3660 QString cover_file;
3661 QString inetref = metadata->GetInetRef();
3662 QString filename = metadata->GetFilename();
3663 QString title = metadata->GetTitle();
3664 int season = metadata->GetSeason();
3665 QString host = metadata->GetHost();
3666 int episode = metadata->GetEpisode();
3667
3668 if (metadata->GetCoverFile().isEmpty() ||
3669 IsDefaultCoverFile(metadata->GetCoverFile()))
3670 {
3671 if (GetLocalVideoImage(inetref, filename,
3672 cover_dirs, cover_file, title,
3673 season, host, "Coverart", episode))
3674 {
3675 metadata->SetCoverFile(cover_file);
3676 OnVideoImageSetDone(metadata);
3677 }
3678 }
3679
3680 QStringList fanart_dirs;
3681 fanart_dirs += m_d->m_fanDir;
3682
3683 QString fanart_file;
3684
3685 if (metadata->GetFanart().isEmpty())
3686 {
3687 if (GetLocalVideoImage(inetref, filename,
3688 fanart_dirs, fanart_file, title,
3689 season, host, "Fanart", episode))
3690 {
3691 metadata->SetFanart(fanart_file);
3692 OnVideoImageSetDone(metadata);
3693 }
3694 }
3695
3696 QStringList banner_dirs;
3697 banner_dirs += m_d->m_banDir;
3698
3699 QString banner_file;
3700
3701 if (metadata->GetBanner().isEmpty())
3702 {
3703 if (GetLocalVideoImage(inetref, filename,
3704 banner_dirs, banner_file, title,
3705 season, host, "Banners", episode))
3706 {
3707 metadata->SetBanner(banner_file);
3708 OnVideoImageSetDone(metadata);
3709 }
3710 }
3711
3712 QStringList screenshot_dirs;
3713 screenshot_dirs += m_d->m_sshotDir;
3714
3715 QString screenshot_file;
3716
3717 if (metadata->GetScreenshot().isEmpty())
3718 {
3719 if (GetLocalVideoImage(inetref, filename,
3720 screenshot_dirs, screenshot_file, title,
3721 season, host, "Screenshots", episode,
3722 true))
3723 {
3724 metadata->SetScreenshot(screenshot_file);
3725 OnVideoImageSetDone(metadata);
3726 }
3727 }
3728}
3729
3731{
3732 if (!lookup)
3733 return;
3734
3735 auto *metadata = lookup->GetData().value<VideoMetadata *>();
3736
3737 if (!metadata)
3738 return;
3739
3740 dismissFetchDialog(metadata, true);
3741 metadata->SetTitle(lookup->GetTitle());
3742 metadata->SetSubtitle(lookup->GetSubtitle());
3743
3744 if (metadata->GetTagline().isEmpty())
3745 metadata->SetTagline(lookup->GetTagline());
3746 if (metadata->GetYear() == 1895 || metadata->GetYear() == 0)
3747 metadata->SetYear(lookup->GetYear());
3748 if (metadata->GetReleaseDate() == QDate())
3749 metadata->SetReleaseDate(lookup->GetReleaseDate());
3750 if (metadata->GetDirector() == VIDEO_DIRECTOR_UNKNOWN ||
3751 metadata->GetDirector().isEmpty())
3752 {
3753 QList<PersonInfo> director = lookup->GetPeople(kPersonDirector);
3754 if (director.count() > 0)
3755 metadata->SetDirector(director.takeFirst().name);
3756 }
3757 if (metadata->GetStudio().isEmpty())
3758 {
3759 QStringList studios = lookup->GetStudios();
3760 if (studios.count() > 0)
3761 metadata->SetStudio(studios.takeFirst());
3762 }
3763 if (metadata->GetPlot() == VIDEO_PLOT_DEFAULT ||
3764 metadata->GetPlot().isEmpty())
3765 metadata->SetPlot(lookup->GetDescription());
3766 if (metadata->GetUserRating() == 0)
3767 metadata->SetUserRating(lookup->GetUserRating());
3768 if (metadata->GetRating() == VIDEO_RATING_DEFAULT)
3769 metadata->SetRating(lookup->GetCertification());
3770 if (metadata->GetLength() == 0min)
3771 metadata->SetLength(lookup->GetRuntime());
3772 if (metadata->GetSeason() == 0)
3773 metadata->SetSeason(lookup->GetSeason());
3774 if (metadata->GetEpisode() == 0)
3775 metadata->SetEpisode(lookup->GetEpisode());
3776 if (metadata->GetHomepage().isEmpty())
3777 metadata->SetHomepage(lookup->GetHomepage());
3778
3779 metadata->SetInetRef(lookup->GetInetref());
3780
3782
3783 // Cast
3784 QList<PersonInfo> actors = lookup->GetPeople(kPersonActor);
3785 QList<PersonInfo> gueststars = lookup->GetPeople(kPersonGuestStar);
3786
3787 for (const auto & name : std::as_const(gueststars))
3788 actors.append(name);
3789
3791 QStringList cl;
3792
3793 for (const auto & person : std::as_const(actors))
3794 cl.append(person.name);
3795
3796 for (const auto & name : std::as_const(cl))
3797 {
3798 QString cn = name.trimmed();
3799 if (!cn.isEmpty())
3800 {
3801 cast.emplace_back(-1, cn);
3802 }
3803 }
3804
3805 metadata->SetCast(cast);
3806
3807 // Genres
3808 VideoMetadata::genre_list video_genres;
3809 QStringList genres = lookup->GetCategories();
3810
3811 for (const auto & name : std::as_const(genres))
3812 {
3813 QString genre_name = name.trimmed();
3814 if (!genre_name.isEmpty())
3815 {
3816 video_genres.emplace_back(-1, genre_name);
3817 }
3818 }
3819
3820 metadata->SetGenres(video_genres);
3821
3822 // Countries
3823 VideoMetadata::country_list video_countries;
3824 QStringList countries = lookup->GetCountries();
3825
3826 for (const auto & name : std::as_const(countries))
3827 {
3828 QString country_name = name.trimmed();
3829 if (!country_name.isEmpty())
3830 {
3831 video_countries.emplace_back(-1, country_name);
3832 }
3833 }
3834
3835 metadata->SetCountries(video_countries);
3836 metadata->SetProcessed(true);
3837
3838 metadata->UpdateDatabase();
3839
3840 MythUIButtonListItem *item = GetItemByMetadata(metadata);
3841 if (item != nullptr)
3842 UpdateItem(item);
3843
3844 StartVideoImageSet(metadata);
3845}
3846
3848{
3849 if (!m_d->m_scanner)
3850 m_d->m_scanner = new VideoScanner();
3853}
3854
3856{
3857 QString message = tr("There are no videos in the database, would you like "
3858 "to scan your video directories now?");
3859 auto *dialog = new MythConfirmationDialog(m_popupStack, message, true);
3860 dialog->SetReturnEvent(this, "scanprompt");
3861 if (dialog->Create())
3862 m_popupStack->AddScreen(dialog);
3863 else
3864 delete dialog;
3865}
3866
3867#include "videodlg.moc"
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:40
static const Type kEventType
Definition: mythdialogbox.h:55
void getExtensionIgnoreList(ext_ignore_list &ext_ignore) const
Definition: dbaccess.cpp:820
std::vector< std::pair< QString, bool > > ext_ignore_list
Definition: dbaccess.h:155
static FileAssociations & getFileAssociation()
Definition: dbaccess.cpp:840
static const Type kEventType
ItemDetailPopup(MythScreenStack *lparent, VideoMetadata *metadata, const VideoMetadataListManager &listManager, PlaybackState &playbackState)
Definition: videodlg.cpp:615
bool Create() override
Definition: videodlg.cpp:622
const VideoMetadataListManager & m_listManager
Definition: videodlg.cpp:702
static const char *const kWindowName
Definition: videodlg.cpp:700
static bool Exists()
Definition: videodlg.cpp:608
bool keyPressEvent(QKeyEvent *levent) override
Key event handler.
Definition: videodlg.cpp:682
MythUIButton * m_doneButton
Definition: videodlg.cpp:706
PlaybackState & m_playbackState
Definition: videodlg.cpp:703
VideoMetadata * m_metadata
Definition: videodlg.cpp:701
bool OnKeyAction(const QStringList &actions)
Definition: videodlg.cpp:666
MythUIButton * m_playButton
Definition: videodlg.cpp:705
static const Type kEventType
static const Type kEventType
static const Type kEventType
void Lookup(ProgramInfo *pginfo, bool automatic=true, bool getimages=true, bool allowgeneric=false)
uint GetSeason() const
QString GetDescription() const
float GetUserRating() const
QStringList GetStudios() const
QString GetTagline() const
QVariant GetData() const
QString GetSubtitle() const
uint GetYear() const
QString GetCertification() const
QDate GetReleaseDate() const
QString GetTitle() const
QStringList GetCountries() const
QString GetInetref() const
QString GetHomepage() const
QStringList GetCategories() const
QList< PersonInfo > GetPeople(PeopleType type) const
std::chrono::minutes GetRuntime() const
uint GetEpisode() const
void haveResult(RefCountHandler< MetadataLookup >)
Dialog asking for user confirmation.
void SaveSetting(const QString &key, int newValue)
void TVPlaybackAborted(void)
QString GetSetting(const QString &key, const QString &defaultval="")
void TVPlaybackStopped(void)
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
int GetNumSetting(const QString &key, int defaultval=0)
static void ClearBackendServerPortCache()
bool GetBoolSetting(const QString &key, bool defaultval=false)
Basic menu dialog, message and a list of options.
void Closed(QString, int)
bool Create(void) override
MythGenericTree * getVisibleChildAt(uint reference) const
int getInt() const
QVariant GetData(void) const
QString GetText(const QString &name="") const
MythGenericTree * getParent(void) const
QStringList getRouteByString(void)
MythGenericTree * getChildAt(uint reference) const
void deleteNode(MythGenericTree *child)
int childCount(void) const
MythGenericTree * getSelectedChild(bool onlyVisible=false) const
uint visibleChildCount() const
void becomeSelectedChild(void)
MythGenericTree * getChildByName(const QString &a_name) const
QList< MythGenericTree * > * getAllChildren() const
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.
MythScreenStack * GetStack(const QString &Stackname)
bool HandleMedia(const QString &Handler, const QString &Mrl, const QString &Plot="", const QString &Title="", const QString &Subtitle="", const QString &Director="", int Season=0, int Episode=0, const QString &Inetref="", std::chrono::minutes LenMins=2h, const QString &Year="1895", const QString &Id="", bool UseBookmarks=false)
void AllowInput(bool Allow)
void UnRegister(void *from, int id, bool closeimemdiately=false)
Unregister the client.
int Register(void *from)
An application can register in which case it will be assigned a reusable screen, which can be modifie...
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
void SetId(int Id)
Contains the application registration id.
void SetParent(void *Parent)
Contains the parent address. Required if id is set Id provided must match the parent address as provi...
virtual void PopScreen(MythScreenType *screen=nullptr, bool allowFade=true, bool deleteScreen=true)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Screen in which all other widgets are contained and rendered.
void BuildFocusList(void)
MythUIType * GetFocusWidget(void) const
MythScreenStack * GetScreenStack() const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
bool SetFocusWidget(MythUIType *widget=nullptr)
virtual void Close()
bool Create(void) override
void DisplayState(const QString &state, const QString &name)
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
MythUIButtonList * parent() 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 itemVisible(MythUIButtonListItem *item)
void SetItemCurrent(MythUIButtonListItem *item)
void RemoveItem(MythUIButtonListItem *item)
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
int GetCurrentPos() const
void itemClicked(MythUIButtonListItem *item)
MythUIButtonListItem * GetItemAt(int pos) const
void itemSelected(MythUIButtonListItem *item)
MythGenericTree * GetCurrentNode(void) const
bool SetCurrentNode(MythGenericTree *node)
Set the currently selected node.
void itemVisible(MythUIButtonListItem *item)
void nodeChanged(MythGenericTree *node)
bool SetNodeByString(QStringList route)
Using a path based on the node string, set the currently selected node.
void itemSelected(MythUIButtonListItem *item)
void itemClicked(MythUIButtonListItem *item)
void RemoveItem(MythUIButtonListItem *item, bool deleteNode=false)
Remove the item from the tree.
void SetActive(bool active)
Set the widget active/inactive.
bool AssignTree(MythGenericTree *tree)
Assign the root node of the tree to be displayed.
MythUIButtonListItem * GetItemCurrent(void) const
Return the currently selected list item.
A single button widget.
Definition: mythuibutton.h:22
void Clicked()
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 Set(int start, int total, int used)
Provide a dialog to quickly find an entry in a list.
void haveResult(QString)
virtual void SetVisible(bool visible)
void SigResultReady(bool passwordValid, ParentalLevel::Level newLevel)
Level GetLevel() const
Utility class to query playback state from database.
Definition: playbackstate.h:12
void Initialize()
Initializes playback state from database.
void Update(const QString &filename)
Updates playback state of video with specified filename.
bool AlwaysShowWatchedProgress() const
Returns cached setting "AlwaysShowWatchedProgress".
bool HasBookmark(const QString &filename) const
Query bookmark of video with the specified filename.
uint GetWatchedPercent(const QString &filename) const
Query watched percent of video with the specified filename.
static bool remoteGetFileList(const QString &host, const QString &path, QStringList *list, QString sgroup, bool fileNamesOnly=false)
static void ClearGroupToUseCache(void)
static QString generate_file_url(const QString &storage_group, const QString &host, const QString &path)
static bool rating_to_pl_greater(const parental_level_map::value_type &lhs, const parental_level_map::value_type &rhs)
Definition: videodlg.cpp:716
VideoDialogPrivate(const VideoListPtr &videoList, VideoDialog::DialogType type, VideoDialog::BrowseType browse)
Definition: videodlg.cpp:725
PlaybackState m_playbackState
Definition: videodlg.cpp:832
parental_level_map m_ratingToPl
Definition: videodlg.cpp:835
static VideoDialog::VideoListDeathDelayPtr m_savedPtr
Definition: videodlg.cpp:801
void AutomaticParentalAdjustment(VideoMetadata *metadata)
Definition: videodlg.cpp:775
std::list< std::pair< QString, ParentalLevel::Level > > parental_level_map
Definition: videodlg.cpp:714
static void DelayVideoListDestruction(const VideoListPtr &videoList)
Definition: videodlg.cpp:792
VideoDialog::DialogType m_type
Definition: videodlg.cpp:818
QString m_lastTreeNodePath
Definition: videodlg.cpp:829
ParentalLevelNotifyContainer m_parentalLevel
Definition: videodlg.cpp:798
VideoScanner * m_scanner
Definition: videodlg.cpp:827
VideoListPtr m_videoList
Definition: videodlg.cpp:807
VideoDialog::BrowseType m_browse
Definition: videodlg.cpp:819
QMap< QString, int > m_notifications
Definition: videodlg.cpp:830
MythGenericTree * m_currentNode
Definition: videodlg.cpp:810
MythGenericTree * m_rootNode
Definition: videodlg.cpp:809
void SwitchManager()
Switch to Video Manager View.
Definition: videodlg.cpp:2881
void OnParentalChange(int amount)
Definition: videodlg.cpp:3549
void VideoMenu()
Pop up a MythUI "Playback Menu" for MythVideo.
Definition: videodlg.cpp:2414
void playFolder()
Play all the items in the selected folder.
Definition: videodlg.cpp:3124
void ShowMetadataSettings()
Pop up a MythUI Menu for MythVideo Metadata Settings.
Definition: videodlg.cpp:2632
void popupClosed(const QString &which, int result)
Definition: videodlg.cpp:2550
void SwitchVideoUserRatingGroup()
Switch to User Rating browse mode.
Definition: videodlg.cpp:2953
void ShowPlayerSettings()
Pop up a MythUI Menu for MythVideo Player Settings.
Definition: videodlg.cpp:2618
MythUIStateType * m_trailerState
Definition: videodlg.h:214
static VideoMetadata * GetMetadata(MythUIButtonListItem *item)
Retrieve the Database Metadata for a given MythUIButtonListItem.
Definition: videodlg.cpp:3286
void OnVideoImageSetDone(VideoMetadata *metadata)
Definition: videodlg.cpp:3399
bool goBack()
Move one level up in the tree.
Definition: videodlg.cpp:2249
MythUIImage * m_screenshot
Definition: videodlg.h:210
void ResetMetadata()
Definition: videodlg.cpp:3639
void setParentalLevel(ParentalLevel::Level level)
Set the parental level for the library.
Definition: videodlg.cpp:3250
void createBusyDialog(const QString &title)
Create a busy dialog, used during metadata search, etc.
Definition: videodlg.cpp:2073
void SwitchVideoYearGroup()
Switch to Year browse mode.
Definition: videodlg.cpp:2917
void SwitchVideoTVMovieGroup()
Switch to Television/Movie browse mode.
Definition: videodlg.cpp:2971
void handleDynamicDirSelect(MythGenericTree *node)
Request the latest metadata for a folder.
Definition: videodlg.cpp:2802
void fetchVideos()
Build the buttonlist/tree.
Definition: videodlg.cpp:1383
void StartVideoImageSet(VideoMetadata *metadata)
Definition: videodlg.cpp:3652
void SetCurrentNode(MythGenericTree *node)
Switch to a given MythGenericTree node.
Definition: videodlg.cpp:2273
MythMenu * CreateViewMenu()
Create a MythMenu for MythVideo Views.
Definition: videodlg.cpp:2565
void SwitchLayout(DialogType type, BrowseType browse)
Handle a layout or browse mode switch.
Definition: videodlg.cpp:2980
void createFetchDialog(VideoMetadata *metadata)
Create a fetch notification, used during metadata search.
Definition: videodlg.cpp:2091
MythUIText * m_titleText
Definition: videodlg.h:203
MythUIImage * m_banner
Definition: videodlg.h:211
void VideoAutoSearch()
Definition: videodlg.h:106
void EditMetadata()
Definition: videodlg.cpp:3567
QString GetFirstImage(MythGenericTree *node, const QString &type, const QString &gpnode=QString(), int levels=0)
Find the first image of "type" within a folder structure.
Definition: videodlg.cpp:1719
void shiftParental(int amount)
Shift the parental level for the library by an integer amount.
Definition: videodlg.cpp:3259
static QString GetCoverImage(MythGenericTree *node)
A "hunt" for cover art to apply for a folder item.
Definition: videodlg.cpp:1486
void doVideoScan()
Definition: videodlg.cpp:3847
void createOkDialog(const QString &title)
Create a MythUI "OK" Dialog.
Definition: videodlg.cpp:2157
bool DoItemDetailShow()
Display the Item Detail Popup.
Definition: videodlg.cpp:3024
MythUIButtonList * m_videoButtonList
Definition: videodlg.h:200
MythUIStateType * m_studioState
Definition: videodlg.h:219
MythUIImage * m_coverImage
Definition: videodlg.h:209
void SwitchVideoGenreGroup()
Switch to Genre browse mode.
Definition: videodlg.cpp:2899
void refreshData()
Reloads the tree without invalidating the data.
Definition: videodlg.cpp:1130
void ToggleFlatView()
Toggle Flat View.
Definition: videodlg.cpp:2777
virtual void loadData()
load the data used to build the ButtonTree/List for MythVideo.
Definition: videodlg.cpp:1182
void RemoveVideo()
Definition: videodlg.cpp:3585
static QString GetBanner(MythGenericTree *node)
Find the Banner for a given node.
Definition: videodlg.cpp:1892
void SwitchVideoCategoryGroup()
Switch to Category browse mode.
Definition: videodlg.cpp:2908
MythDialogBox * m_menuPopup
Definition: videodlg.h:195
void handleDirSelect(MythGenericTree *node)
Descend into a selected folder.
Definition: videodlg.cpp:2792
void DisplayMenu()
Pop up a MythUI Menu for MythVideo Global Functions.
Definition: videodlg.cpp:2521
void searchComplete(const QString &string)
After using incremental search, move to the selected item.
Definition: videodlg.cpp:2171
void ShowCastDialog()
Display the Cast if the selected item.
Definition: videodlg.cpp:3048
void customEvent(QEvent *levent) override
Definition: videodlg.cpp:3305
void SwitchVideoStudioGroup()
Switch to Studio browse mode.
Definition: videodlg.cpp:2935
MythUIText * m_positionText
Definition: videodlg.h:206
void SavePosition(void)
Definition: videodlg.cpp:940
MythMenu * CreateMetadataBrowseMenu()
Create a MythMenu for MythVideo Metadata Browse modes.
Definition: videodlg.cpp:2660
@ DLG_DEFAULT
Definition: videodlg.h:38
@ DLG_MANAGER
Definition: videodlg.h:39
@ DLG_GALLERY
Definition: videodlg.h:38
@ DLG_BROWSER
Definition: videodlg.h:38
void SwitchTree()
Switch to Tree (List) View.
Definition: videodlg.cpp:2854
static QString GetScreenshot(MythGenericTree *node)
Find the Screenshot for a given node.
Definition: videodlg.cpp:1852
MythMenu * CreateSettingsMenu()
Create a MythMenu for MythVideo Settings.
Definition: videodlg.cpp:2601
static VideoListDeathDelayPtr & GetSavedVideoList()
Definition: videodlg.cpp:879
QPointer< class VideoListDeathDelay > VideoListDeathDelayPtr
Definition: videodlg.h:48
static void UpdateWatchedState(MythUIButtonListItem *item)
Update the watched state for a given ButtonListItem from the database.
Definition: videodlg.cpp:2390
void reloadData()
Reloads the tree after having invalidated the data.
Definition: videodlg.cpp:1172
void SwitchVideoFolderGroup()
Switch to Folder (filesystem) browse mode.
Definition: videodlg.cpp:2890
virtual MythUIButtonListItem * GetItemCurrent()
Definition: videodlg.cpp:3412
MythMenu * CreateManageMenu()
Create a MythMenu for metadata management.
Definition: videodlg.cpp:2730
void ShowHomepage()
Definition: videodlg.cpp:3058
static QString GetFanart(MythGenericTree *node)
Find the Fanart for a given node.
Definition: videodlg.cpp:1927
void UpdateItem(MythUIButtonListItem *item)
Update the visible representation of a MythUIButtonListItem.
Definition: videodlg.cpp:1289
void PromptToScan()
Definition: videodlg.cpp:3855
void UpdateVisible(MythUIButtonListItem *item)
Update playback state for for a given visible ButtonListItem.
Definition: videodlg.cpp:2302
void playTrailer()
Play the trailer associated with the selected item.
Definition: videodlg.cpp:3227
MythUIStateType * m_parentalLevelState
Definition: videodlg.h:215
void SwitchVideoCastGroup()
Switch to Cast browse mode.
Definition: videodlg.cpp:2944
void OnPlaybackStopped()
Definition: videodlg.cpp:917
void playVideoWithTrailers()
Play the selected item w/ a User selectable # of trailers.
Definition: videodlg.cpp:3194
MythUIText * m_crumbText
Definition: videodlg.h:207
~VideoDialog() override
Definition: videodlg.cpp:930
void ViewPlot()
Display a MythUI Popup with the selected item's plot.
Definition: videodlg.cpp:3010
void OnVideoSearchDone(MetadataLookup *lookup)
Definition: videodlg.cpp:3730
void UpdatePosition()
Called after the screen is created by MythScreenStack.
Definition: videodlg.cpp:2285
class VideoDialogPrivate * m_d
Definition: videodlg.h:224
void OnVideoSearchListSelection(RefCountHandler< MetadataLookup > lookup)
Definition: videodlg.cpp:3520
void SwitchGallery()
Switch to Gallery View.
Definition: videodlg.cpp:2863
void UpdateText(MythUIButtonListItem *item)
Update the visible text values for a given ButtonListItem.
Definition: videodlg.cpp:2318
void ChangeFilter()
Change the filtering of the library.
Definition: videodlg.cpp:3269
void handleSelect(MythUIButtonListItem *item)
Handle SELECT action for a given MythUIButtonListItem.
Definition: videodlg.cpp:2820
void Init() override
Definition: videodlg.cpp:1111
void playbackStateChanged(const QString &filename)
Definition: videodlg.cpp:906
MythUIImage * m_fanart
Definition: videodlg.h:212
static QString RemoteImageCheck(const QString &host, const QString &filename)
Search for a given (image) filename in the Video SG.
Definition: videodlg.cpp:1429
void SwitchBrowse()
Switch to Browser View.
Definition: videodlg.cpp:2872
void playVideoAlt()
Play the selected item in an alternate player.
Definition: videodlg.cpp:3113
void ToggleProcess()
Definition: videodlg.cpp:2749
void dismissFetchDialog(VideoMetadata *metadata, bool ok)
Definition: videodlg.cpp:2113
simple_ref_ptr< class VideoList > VideoListPtr
Definition: videodlg.h:47
void playVideo()
Play the selected item.
Definition: videodlg.cpp:3102
void searchStart()
Create an incremental search dialog for the current tree level.
Definition: videodlg.cpp:2211
MythMenu * CreatePlayMenu()
Create a "Play Menu" for MythVideo.
Definition: videodlg.cpp:2483
void SwitchVideoInsertDateGroup()
Switch to Insert Date browse mode.
Definition: videodlg.cpp:2962
MetadataFactory * m_metadataFactory
Definition: videodlg.h:222
void VideoSearch()
Definition: videodlg.h:104
bool keyPressEvent(QKeyEvent *levent) override
Handle keypresses and keybindings.
Definition: videodlg.cpp:1962
void ToggleBrowseMode()
Toggle the browseable status for the selected item.
Definition: videodlg.cpp:2765
MythMenu * CreateInfoMenu()
Create a MythMenu for Info pertaining to the selected item.
Definition: videodlg.cpp:2703
VideoDialog(MythScreenStack *lparent, const QString &lname, const VideoListPtr &video_list, DialogType type, BrowseType browse)
Definition: videodlg.cpp:884
void OnRemoveVideo(bool dodelete)
Definition: videodlg.cpp:3604
MythScreenStack * m_mainStack
Definition: videodlg.h:198
MythUIStateType * m_watchedState
Definition: videodlg.h:218
void DoItemDetailShow2()
Definition: videodlg.h:135
MythUIStateType * m_bookmarkState
Definition: videodlg.h:220
void Load() override
Called after the screen is created by MythScreenStack.
Definition: videodlg.cpp:1117
MythUIBusyDialog * m_busyPopup
Definition: videodlg.h:196
bool Create() override
Definition: videodlg.cpp:964
void ToggleWatched()
Definition: videodlg.cpp:3504
virtual MythUIButtonListItem * GetItemByMetadata(VideoMetadata *metadata)
Definition: videodlg.cpp:3422
void scanFinished(bool dbChanged)
Definition: videodlg.cpp:1145
@ BRS_USERRATING
Definition: videodlg.h:44
@ BRS_CATEGORY
Definition: videodlg.h:42
@ BRS_STUDIO
Definition: videodlg.h:45
@ BRS_FOLDER
Definition: videodlg.h:42
@ BRS_DIRECTOR
Definition: videodlg.h:43
@ BRS_TVMOVIE
Definition: videodlg.h:45
@ BRS_INSERTDATE
Definition: videodlg.h:44
MythScreenStack * m_popupStack
Definition: videodlg.h:197
MythUIButtonTree * m_videoButtonTree
Definition: videodlg.h:201
void ShowExtensionSettings()
Pop up a MythUI Menu for MythVideo filte Type Settings.
Definition: videodlg.cpp:2646
void SwitchVideoDirectorGroup()
Switch to Director browse mode.
Definition: videodlg.cpp:2926
MythUIText * m_novideoText
Definition: videodlg.h:204
VideoListDeathDelayPrivate(const VideoDialog::VideoListPtr &toSave)
Definition: videodlg.cpp:843
VideoDialog::VideoListPtr GetSaved()
Definition: videodlg.cpp:848
VideoDialog::VideoListPtr m_savedList
Definition: videodlg.cpp:854
~VideoListDeathDelay() override
Definition: videodlg.cpp:864
VideoDialog::VideoListPtr GetSaved()
Definition: videodlg.cpp:869
static constexpr std::chrono::milliseconds kDelayTimeMS
Definition: videodlg.h:239
VideoListDeathDelay(const VideoDialog::VideoListPtr &toSave)
Definition: videodlg.cpp:857
class VideoListDeathDelayPrivate * m_d
Definition: videodlg.h:245
bool Delete(int video_id)
Definition: videolist.cpp:513
bool refreshNode(MythGenericTree *node)
Definition: videolist.cpp:478
void InvalidateCache()
Definition: videolist.cpp:523
const VideoMetadataListManager & getListCache() const
Definition: videolist.cpp:503
void setCurrentVideoFilter(const VideoFilterSettings &filter)
Definition: videolist.cpp:493
void refreshList(bool filebrowser, const ParentalLevel &parental_level, bool flat_list, int group_type)
Definition: videolist.cpp:471
MythGenericTree * GetTreeRoot()
Definition: videolist.cpp:518
MythGenericTree * buildVideoList(bool filebrowser, bool flatlist, int group_type, const ParentalLevel &parental_level, bool include_updirs)
Definition: videolist.cpp:462
VideoMetadataPtr byFilename(const QString &file_name) const
VideoMetadataPtr byID(unsigned int db_id) const
static VideoMetadataPtr loadOneFromDatabase(uint id)
const QString & GetHost() const
bool IsHostSet() const
bool GetProcessed() const
const QString & GetCoverFile() const
void SetCoverFile(const QString &coverFile)
bool GetWatched() const
const QString & GetTitle() const
const QString & GetStudio() const
void SetFanart(const QString &fanart)
unsigned int GetID() const
const QString & GetFanart() const
std::vector< cast_entry > cast_list
Definition: videometadata.h:34
ParentalLevel::Level GetShowLevel() const
void SetShowLevel(ParentalLevel::Level showLevel)
const QString & GetHash() const
const cast_list & GetCast() const
const QString & GetInetRef() const
const QString & GetBanner() const
const QString & GetTrailer() const
const QString & GetRating() const
void SetProcessed(bool processed)
void toMap(InfoMap &metadataMap)
const QString & GetScreenshot() const
const QString & GetSubtitle() const
void SetScreenshot(const QString &screenshot)
int GetSeason() const
std::vector< genre_entry > genre_list
Definition: videometadata.h:32
const QString & GetHomepage() const
void SetBanner(const QString &banner)
std::vector< country_entry > country_list
Definition: videometadata.h:33
void SetWatched(bool watched)
int GetEpisode() const
const QString & GetFilename() const
float GetUserRating() const
void Reset()
Resets to default metadata.
static VideoPlayerCommand AltPlayerFor(const VideoMetadata *item)
static VideoPlayerCommand PlayerFor(const VideoMetadata *item)
void finished(bool)
void doScan(const QStringList &dirs)
Definition: videoscan.cpp:423
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
void LoadImage(const QString &filename, MythUIImage *image)
Definition: videodlg.cpp:312
void handleText(const QString &name, const QString &value) override
Definition: videodlg.cpp:434
void handleState(const QString &name, const QString &value) override
Definition: videodlg.cpp:439
void handleImage(const QString &name, const QString &filename) override
Definition: videodlg.cpp:444
void OnResultReady(bool passwordValid, ParentalLevel::Level newLevel)
Definition: videodlg.cpp:97
void handleImage(const QString &name, const QString &filename) override
Definition: videodlg.cpp:397
void handleState(const QString &name, const QString &value) override
Definition: videodlg.cpp:392
void handleText(const QString &name, const QString &value) override
Definition: videodlg.cpp:387
T * get() const
Definition: quicksp.h:73
bool ScanVideoDirectory(const QString &start_path, DirectoryHandler *handler, const FileAssociations::ext_ignore_list &ext_disposition, bool list_unknown_extensions)
Definition: dirscan.cpp:227
const QString VIDEO_PLOT_DEFAULT
Definition: globals.cpp:32
const QString VIDEO_BANNER_DEFAULT
Definition: globals.cpp:28
const QString VIDEO_SCREENSHOT_DEFAULT
Definition: globals.cpp:27
const QString VIDEO_FANART_DEFAULT
Definition: globals.cpp:29
const QString VIDEO_RATING_DEFAULT
Definition: globals.cpp:30
const QString VIDEO_DIRECTOR_UNKNOWN
Definition: globals.cpp:9
static const iso6937table * d
@ kLookupData
@ kProbableTelevision
@ kUnknownVideo
@ kProbableMovie
@ kMetadataRecording
@ kMetadataVideo
@ kPersonActor
@ kPersonDirector
@ kPersonGuestStar
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythNotificationCenter * GetNotificationCenter(void)
MythMainWindow * GetMythMainWindow(void)
Convenience inline random number generator functions.
@ kMSDontDisableDrawing
avoid disabling UI drawing
Definition: mythsystem.h:37
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
static MythThemedMenu * menu
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
QStringList GetTrailersInDirectory(const QString &startDir)
Definition: videodlg.cpp:3177
VideoMetadata * GetMetadataPtrFromNode(MythGenericTree *node)
Definition: videodlg.cpp:129
void CopyMetadataToUI(const VideoMetadata *metadata, CopyMetadataDestination &dest)
Definition: videodlg.cpp:453
void PlayVideo(const QString &filename, const VideoMetadataListManager &video_list, bool useAltPlayer=false)
Definition: videodlg.cpp:269
void CopyPlaybackStateToUI(const PlaybackState &playbackState, const VideoMetadata *metadata, MythUIButtonListItem *item=nullptr, MythScreenType *screen=nullptr)
Definition: videodlg.cpp:572
MythGenericTree * GetNodePtrFromButton(MythUIButtonListItem *item)
Definition: videodlg.cpp:121
bool GetLocalVideoImage(const QString &video_uid, const QString &filename, const QStringList &in_dirs, QString &image, QString title, int season, const QString &host, const QString &sgroup, int episode=0, bool isScreenshot=false)
Definition: videodlg.cpp:137
std::unique_ptr< FanartLoader > fanartLoader
Definition: videodlg.cpp:372
def rating(profile, smoonURL, gate)
Definition: scan.py:36
bool exists(str path)
Definition: xbmcvfs.py:51
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
virtual void handleState(const QString &name, const QString &value)=0
virtual void handleImage(const QString &name, const QString &filename)=0
virtual void handleText(const QString &name, const QString &value)=0
void handleFile(const QString &fileName, const QString &fqFileName, const QString &extension, const QString &host) override
Definition: videodlg.cpp:3165
DirectoryHandler * newDir(const QString &dirName, const QString &fqDirName) override
Definition: videodlg.cpp:3159
#define LOC_MML
Definition: videodlg.cpp:60
static const QString sLocation
Definition: videodlg.cpp:62
@ kSubFolder
Definition: videolist.h:6
@ kUpFolder
Definition: videolist.h:7
@ kDynamicSubFolder
Definition: videolist.h:10
@ kNoFilesFound
Definition: videolist.h:9
void ClearMap(InfoMap &metadataMap)
bool IsDefaultScreenshot(const QString &screenshot)
Definition: videoutils.cpp:136
QString ParentalLevelToState(const ParentalLevel &level)
Definition: videoutils.cpp:239
bool IsDefaultFanart(const QString &fanart)
Definition: videoutils.cpp:146
QString WatchedToState(bool watched)
Definition: videoutils.cpp:273
QStringList GetVideoDirsByHost(const QString &host)
Definition: videoutils.cpp:82
QString TrailerToState(const QString &trailerFile)
Definition: videoutils.cpp:263
void CheckedSet(MythUIStateType *uiItem, const QString &value)
Definition: videoutils.cpp:44
bool IsDefaultCoverFile(const QString &coverfile)
Definition: videoutils.cpp:127
bool IsDefaultBanner(const QString &banner)
Definition: videoutils.cpp:141
QStringList GetVideoDirs()
Definition: videoutils.cpp:122