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