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 QUrl sgurl = dir;
1442 QString path = sgurl.path();
1443
1444 QString fname = QString("%1/%2").arg(path, filename);
1445
1446 QStringList list( QString("QUERY_SG_FILEQUERY") );
1447 list << host;
1448 list << "Videos";
1449 list << fname;
1450
1451 bool ok = gCoreContext->SendReceiveStringList(list);
1452
1453 if (!ok || list.at(0).startsWith("SLAVE UNREACHABLE"))
1454 {
1455 LOG(VB_GENERAL, LOG_WARNING,
1456 QString("Backend : %1 currently Unreachable. Skipping "
1457 "this one.") .arg(host));
1458 break;
1459 }
1460
1461 if ((!list.isEmpty()) && (list.at(0) == fname))
1462 result = StorageGroup::generate_file_url("Videos", host, filename);
1463
1464 if (!result.isEmpty())
1465 {
1466#if 0
1467 LOG(VB_GENERAL, LOG_DEBUG,
1468 QString("RemoteImageCheck(%1) res :%2: :%3:")
1469 .arg(fname).arg(result).arg(dir));
1470#endif
1471 break;
1472 }
1473
1474 }
1475 }
1476
1477 return result;
1478}
1479
1485{
1486 if (!node)
1487 return {};
1488
1489 int nodeInt = node->getInt();
1490
1491 QString icon_file;
1492
1493 if (nodeInt == kSubFolder) // subdirectory
1494 {
1495 // First validate that the data can be converted
1496 if (!node->GetData().canConvert<TreeNodeData>())
1497 return icon_file;
1498
1499 // load folder icon
1500 QString folder_path = node->GetData().value<TreeNodeData>().GetPath();
1501 QString host = node->GetData().value<TreeNodeData>().GetHost();
1502 QString prefix = node->GetData().value<TreeNodeData>().GetPrefix();
1503
1504 if (folder_path.startsWith("myth://"))
1505 folder_path = folder_path.right(folder_path.length()
1506 - folder_path.lastIndexOf("//") - 1);
1507
1508 QString filename = QString("%1/folder").arg(folder_path);
1509
1510#if 0
1511 LOG(VB_GENERAL, LOG_DEBUG,
1512 QString("GetCoverImage host : %1 prefix : %2 file : %3")
1513 .arg(host).arg(prefix).arg(filename));
1514#endif
1515
1516 QStringList test_files;
1517 test_files.append(filename + ".png");
1518 test_files.append(filename + ".jpg");
1519 test_files.append(filename + ".jpeg");
1520 test_files.append(filename + ".gif");
1521
1522 // coverity[auto_causes_copy]
1523 for (auto imagePath : std::as_const(test_files))
1524 {
1525#if 0
1526 LOG(VB_GENERAL, LOG_DEBUG, QString("Cover check :%1 : ").arg(imagePath));
1527#endif
1528
1529 bool foundCover = false;
1530 if (!host.isEmpty())
1531 {
1532 // Strip out any extra /'s
1533 imagePath.replace("//", "/");
1534 prefix.replace("//","/");
1535 imagePath = imagePath.right(imagePath.length() - (prefix.length() + 1));
1536 QString tmpCover = RemoteImageCheck(host, imagePath);
1537
1538 if (!tmpCover.isEmpty())
1539 {
1540 foundCover = true;
1541 imagePath = tmpCover;
1542 }
1543 }
1544 else
1545 {
1546 foundCover = QFile::exists(imagePath);
1547 }
1548
1549 if (foundCover)
1550 {
1551 icon_file = imagePath;
1552 break;
1553 }
1554 }
1555
1556 // If we found nothing, load the first image we find
1557 if (icon_file.isEmpty())
1558 {
1559 QStringList imageTypes { "*.png", "*.jpg", "*.jpeg", "*.gif" };
1560 QStringList fList;
1561
1562 if (!host.isEmpty())
1563 {
1564 // TODO: This can likely get a little cleaner
1565
1566 QStringList dirs = GetVideoDirsByHost(host);
1567
1568 if (!dirs.isEmpty())
1569 {
1570 for (const auto & dir : std::as_const(dirs))
1571 {
1572 QUrl sgurl = dir;
1573 QString path = sgurl.path();
1574
1575 QString subdir = folder_path.right(folder_path.length() - (prefix.length() + 1));
1576
1577 path = path + "/" + subdir;
1578
1579 QStringList tmpList;
1580 bool ok = StorageGroup::remoteGetFileList(host, path, &tmpList, "Videos");
1581
1582 if (ok)
1583 {
1584 for (const auto & pattern : std::as_const(imageTypes))
1585 {
1586 auto rePattern = QRegularExpression::wildcardToRegularExpression(pattern);
1587 QRegularExpression rx {
1588 rePattern.mid(2,rePattern.size()-4), // Remove anchors
1589 QRegularExpression::CaseInsensitiveOption };
1590 QStringList matches = tmpList.filter(rx);
1591 if (!matches.isEmpty())
1592 {
1593 fList.clear();
1594 fList.append(subdir + "/" + matches.at(0).split("::").at(1));
1595 break;
1596 }
1597 }
1598
1599 break;
1600 }
1601 }
1602 }
1603
1604 }
1605 else
1606 {
1607 QDir vidDir(folder_path);
1608 vidDir.setNameFilters(imageTypes);
1609 fList = vidDir.entryList();
1610 }
1611
1612 // Take the Coverfile for the first valid node in the dir, if it exists.
1613 if (icon_file.isEmpty())
1614 {
1615 int list_count = node->visibleChildCount();
1616 if (list_count > 0)
1617 {
1618 for (int i = 0; i < list_count; i++)
1619 {
1620 MythGenericTree *subnode = node->getVisibleChildAt(i);
1621 if (subnode)
1622 {
1623 VideoMetadata *metadata = GetMetadataPtrFromNode(subnode);
1624 if (metadata)
1625 {
1626 if (!metadata->GetHost().isEmpty() &&
1627 !metadata->GetCoverFile().startsWith("/"))
1628 {
1629 QString test_file = StorageGroup::generate_file_url("Coverart",
1630 metadata->GetHost(), metadata->GetCoverFile());
1631 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1632 !IsDefaultCoverFile(test_file))
1633 {
1634 icon_file = test_file;
1635 break;
1636 }
1637 }
1638 else
1639 {
1640 const QString& test_file = metadata->GetCoverFile();
1641 if (!test_file.isEmpty() &&
1642 !IsDefaultCoverFile(test_file))
1643 {
1644 icon_file = test_file;
1645 break;
1646 }
1647 }
1648 }
1649 }
1650 }
1651 }
1652 }
1653
1654 if (!fList.isEmpty())
1655 {
1656 if (host.isEmpty())
1657 {
1658 icon_file = QString("%1/%2").arg(folder_path, fList.at(0));
1659 }
1660 else
1661 {
1662 icon_file = StorageGroup::generate_file_url("Videos", host, fList.at(0));
1663 }
1664 }
1665 }
1666
1667 if (!icon_file.isEmpty())
1668 {
1669 LOG(VB_GENERAL, LOG_DEBUG, QString("Found Image : %1 :")
1670 .arg(icon_file));
1671 }
1672 else
1673 {
1674 LOG(VB_GENERAL, LOG_DEBUG,
1675 QString("Could not find folder cover Image : %1 ")
1676 .arg(folder_path));
1677 }
1678 }
1679 else
1680 {
1681 const VideoMetadata *metadata = GetMetadataPtrFromNode(node);
1682
1683 if (metadata)
1684 {
1685 if (metadata->IsHostSet() &&
1686 !metadata->GetCoverFile().startsWith("/") &&
1687 !IsDefaultCoverFile(metadata->GetCoverFile()))
1688 {
1689 icon_file = StorageGroup::generate_file_url("Coverart", metadata->GetHost(),
1690 metadata->GetCoverFile());
1691 }
1692 else
1693 {
1694 icon_file = metadata->GetCoverFile();
1695 }
1696 }
1697 }
1698
1699 if (IsDefaultCoverFile(icon_file))
1700 icon_file.clear();
1701
1702 return icon_file;
1703}
1704
1717 const QString& gpnode, int levels)
1718{
1719 if (!node || type.isEmpty())
1720 return {};
1721
1722 QString icon_file;
1723
1724 int list_count = node->visibleChildCount();
1725 if (list_count > 0)
1726 {
1727 QList<MythGenericTree *> subDirs;
1728 static constexpr int maxRecurse { 1 };
1729
1730 for (int i = 0; i < list_count; i++)
1731 {
1732 MythGenericTree *subnode = node->getVisibleChildAt(i);
1733 if (subnode)
1734 {
1735 if (subnode->childCount() > 0)
1736 subDirs << subnode;
1737
1738 VideoMetadata *metadata = GetMetadataPtrFromNode(subnode);
1739 if (metadata)
1740 {
1741 QString test_file;
1742 const QString& host = metadata->GetHost();
1743 const QString& title = metadata->GetTitle();
1744
1745 if (type == "Coverart" && !host.isEmpty() &&
1746 !metadata->GetCoverFile().startsWith("/"))
1747 {
1748 test_file = StorageGroup::generate_file_url("Coverart",
1749 host, metadata->GetCoverFile());
1750 }
1751 else if (type == "Coverart")
1752 {
1753 test_file = metadata->GetCoverFile();
1754 }
1755
1756 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1757 !IsDefaultCoverFile(test_file) && (gpnode.isEmpty() ||
1758 (QString::compare(gpnode, title, Qt::CaseInsensitive) == 0)))
1759 {
1760 icon_file = test_file;
1761 break;
1762 }
1763
1764 if (type == "Fanart" && !host.isEmpty() &&
1765 !metadata->GetFanart().startsWith("/"))
1766 {
1767 test_file = StorageGroup::generate_file_url("Fanart",
1768 host, metadata->GetFanart());
1769 }
1770 else if (type == "Fanart")
1771 {
1772 test_file = metadata->GetFanart();
1773 }
1774
1775 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1776 test_file != VIDEO_FANART_DEFAULT && (gpnode.isEmpty() ||
1777 (QString::compare(gpnode, title, Qt::CaseInsensitive) == 0)))
1778 {
1779 icon_file = test_file;
1780 break;
1781 }
1782
1783 if (type == "Banners" && !host.isEmpty() &&
1784 !metadata->GetBanner().startsWith("/"))
1785 {
1786 test_file = StorageGroup::generate_file_url("Banners",
1787 host, metadata->GetBanner());
1788 }
1789 else if (type == "Banners")
1790 {
1791 test_file = metadata->GetBanner();
1792 }
1793
1794 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1795 test_file != VIDEO_BANNER_DEFAULT && (gpnode.isEmpty() ||
1796 (QString::compare(gpnode, title, Qt::CaseInsensitive) == 0)))
1797 {
1798 icon_file = test_file;
1799 break;
1800 }
1801
1802 if (type == "Screenshots" && !host.isEmpty() &&
1803 !metadata->GetScreenshot().startsWith("/"))
1804 {
1805 test_file = StorageGroup::generate_file_url("Screenshots",
1806 host, metadata->GetScreenshot());
1807 }
1808 else if (type == "Screenshots")
1809 {
1810 test_file = metadata->GetScreenshot();
1811 }
1812
1813 if (!test_file.endsWith("/") && !test_file.isEmpty() &&
1814 test_file != VIDEO_SCREENSHOT_DEFAULT && (gpnode.isEmpty() ||
1815 (QString::compare(gpnode, title, Qt::CaseInsensitive) == 0)))
1816 {
1817 icon_file = test_file;
1818 break;
1819 }
1820 }
1821 }
1822 }
1823 if (icon_file.isEmpty() && !subDirs.isEmpty())
1824 {
1825 QString test_file;
1826 int subDirCount = subDirs.count();
1827 for (int i = 0; i < subDirCount; i ++)
1828 {
1829 if (levels < maxRecurse)
1830 {
1831 test_file = GetFirstImage(subDirs[i], type,
1832 node->GetText(), levels + 1);
1833 if (!test_file.isEmpty())
1834 {
1835 icon_file = test_file;
1836 break;
1837 }
1838 }
1839 }
1840 }
1841 }
1842 return icon_file;
1843}
1844
1850{
1851 const int nodeInt = node->getInt();
1852
1853 QString icon_file;
1854
1855 if (nodeInt == kSubFolder || nodeInt == kUpFolder) // subdirectory
1856 {
1857 icon_file = VIDEO_SCREENSHOT_DEFAULT;
1858 }
1859 else
1860 {
1861 const VideoMetadata *metadata = GetMetadataPtrFromNode(node);
1862
1863 if (metadata)
1864 {
1865 if (metadata->IsHostSet() &&
1866 !metadata->GetScreenshot().startsWith("/") &&
1867 !metadata->GetScreenshot().isEmpty())
1868 {
1869 icon_file = StorageGroup::generate_file_url("Screenshots", metadata->GetHost(),
1870 metadata->GetScreenshot());
1871 }
1872 else
1873 {
1874 icon_file = metadata->GetScreenshot();
1875 }
1876 }
1877 }
1878
1879 if (IsDefaultScreenshot(icon_file))
1880 icon_file.clear();
1881
1882 return icon_file;
1883}
1884
1890{
1891 const int nodeInt = node->getInt();
1892
1893 if (nodeInt == kSubFolder || nodeInt == kUpFolder)
1894 return {};
1895
1896 QString icon_file;
1897 const VideoMetadata *metadata = GetMetadataPtrFromNode(node);
1898
1899 if (metadata)
1900 {
1901 if (metadata->IsHostSet() &&
1902 !metadata->GetBanner().startsWith("/") &&
1903 !metadata->GetBanner().isEmpty())
1904 {
1905 icon_file = StorageGroup::generate_file_url("Banners", metadata->GetHost(),
1906 metadata->GetBanner());
1907 }
1908 else
1909 {
1910 icon_file = metadata->GetBanner();
1911 }
1912
1913 if (IsDefaultBanner(icon_file))
1914 icon_file.clear();
1915 }
1916
1917 return icon_file;
1918}
1919
1925{
1926 const int nodeInt = node->getInt();
1927
1928 if (nodeInt == kSubFolder || nodeInt == kUpFolder) // subdirectory
1929 return {};
1930
1931 QString icon_file;
1932 const VideoMetadata *metadata = GetMetadataPtrFromNode(node);
1933
1934 if (metadata)
1935 {
1936 if (metadata->IsHostSet() &&
1937 !metadata->GetFanart().startsWith("/") &&
1938 !metadata->GetFanart().isEmpty())
1939 {
1940 icon_file = StorageGroup::generate_file_url("Fanart", metadata->GetHost(),
1941 metadata->GetFanart());
1942 }
1943 else
1944 {
1945 icon_file = metadata->GetFanart();
1946 }
1947
1948 if (IsDefaultFanart(icon_file))
1949 icon_file.clear();
1950 }
1951
1952 return icon_file;
1953}
1954
1959bool VideoDialog::keyPressEvent(QKeyEvent *levent)
1960{
1961 if (GetFocusWidget()->keyPressEvent(levent))
1962 return true;
1963
1964 QStringList actions;
1965 bool handled = GetMythMainWindow()->TranslateKeyPress("Video", levent, actions);
1966
1967 for (int i = 0; i < actions.size() && !handled; i++)
1968 {
1969 const QString& action = actions[i];
1970 handled = true;
1971
1972 if (action == "INFO")
1973 {
1976 if (!m_menuPopup && node->getInt() != kUpFolder)
1977 VideoMenu();
1978 }
1979 else if (action == "INCPARENT")
1980 {
1981 shiftParental(1);
1982 }
1983 else if (action == "DECPARENT")
1984 {
1985 shiftParental(-1);
1986 }
1987 else if (action == "1" || action == "2" ||
1988 action == "3" || action == "4")
1989 {
1991 }
1992 else if (action == "FILTER")
1993 {
1994 ChangeFilter();
1995 }
1996 else if (action == "MENU")
1997 {
1998 if (!m_menuPopup)
1999 DisplayMenu();
2000 }
2001 else if (action == "PLAYALT")
2002 {
2005 playVideoAlt();
2006 }
2007 else if (action == "DOWNLOADDATA")
2008 {
2010 VideoSearch();
2011 }
2012 else if (action == "INCSEARCH")
2013 {
2014 searchStart();
2015 }
2016 else if (action == "ITEMDETAIL")
2017 {
2019 }
2020 else if (action == "DELETE")
2021 {
2023 RemoveVideo();
2024 }
2025 else if (action == "EDIT" && !m_menuPopup)
2026 {
2027 EditMetadata();
2028 }
2029 else if (action == "ESCAPE")
2030 {
2031 if (m_d->m_type != DLG_TREE
2032 && !GetMythMainWindow()->IsExitingToMain()
2034 handled = goBack();
2035 else
2036 handled = false;
2037 }
2038 else
2039 {
2040 handled = false;
2041 }
2042 }
2043
2044 if (!handled)
2045 {
2046 handled = GetMythMainWindow()->TranslateKeyPress("TV Frontend", levent,
2047 actions);
2048
2049 for (int i = 0; i < actions.size() && !handled; i++)
2050 {
2051 const QString& action = actions[i];
2052 if (action == "PLAYBACK")
2053 {
2054 handled = true;
2055 playVideo();
2056 }
2057 }
2058 }
2059
2060 if (!handled && MythScreenType::keyPressEvent(levent))
2061 handled = true;
2062
2063 return handled;
2064}
2065
2070void VideoDialog::createBusyDialog(const QString &title)
2071{
2072 if (m_busyPopup)
2073 return;
2074
2075 const QString& message = title;
2076
2078 "mythvideobusydialog");
2079
2080 if (m_busyPopup->Create())
2082}
2083
2089{
2090 if (m_d->m_notifications.contains(metadata->GetHash()))
2091 return;
2092
2093 int id = GetNotificationCenter()->Register(this);
2094 m_d->m_notifications[metadata->GetHash()] = id;
2095
2096 QString msg = tr("Fetching details for %1")
2097 .arg(metadata->GetTitle());
2098 QString desc;
2099 if (metadata->GetSeason() > 0 || metadata->GetEpisode() > 0)
2100 {
2101 desc = tr("Season %1, Episode %2")
2102 .arg(metadata->GetSeason()).arg(metadata->GetEpisode());
2103 }
2104 MythBusyNotification n(msg, sLocation, desc);
2105 n.SetId(id);
2106 n.SetParent(this);
2108}
2109
2111{
2112 if (!metadata || !m_d->m_notifications.contains(metadata->GetHash()))
2113 return;
2114
2115 int id = m_d->m_notifications[metadata->GetHash()];
2116 m_d->m_notifications.remove(metadata->GetHash());
2117
2118 QString msg;
2119 if (ok)
2120 {
2121 msg = tr("Retrieved details for %1").arg(metadata->GetTitle());
2122 }
2123 else
2124 {
2125 msg = tr("Failed to retrieve details for %1").arg(metadata->GetTitle());
2126 }
2127 QString desc;
2128 if (metadata->GetSeason() > 0 || metadata->GetEpisode() > 0)
2129 {
2130 desc = tr("Season %1, Episode %2")
2131 .arg(metadata->GetSeason()).arg(metadata->GetEpisode());
2132 }
2133 if (ok)
2134 {
2135 MythCheckNotification n(msg, sLocation, desc);
2136 n.SetId(id);
2137 n.SetParent(this);
2139 }
2140 else
2141 {
2142 MythErrorNotification n(msg, sLocation, desc);
2143 n.SetId(id);
2144 n.SetParent(this);
2146 }
2147 GetNotificationCenter()->UnRegister(this, id);
2148}
2149
2154void VideoDialog::createOkDialog(const QString& title)
2155{
2156 const QString& message = title;
2157
2158 auto *okPopup = new MythConfirmationDialog(m_popupStack, message, false);
2159
2160 if (okPopup->Create())
2161 m_popupStack->AddScreen(okPopup);
2162}
2163
2168void VideoDialog::searchComplete(const QString& string)
2169{
2170 LOG(VB_GENERAL, LOG_DEBUG, QString("Jumping to: %1").arg(string));
2171
2173 QList<MythGenericTree*> *children = nullptr;
2174 QMap<int, QString> idTitle;
2175
2176 if (parent && m_d->m_type == DLG_TREE)
2177 children = parent->getAllChildren();
2178 else
2179 children = m_d->m_currentNode->getAllChildren();
2180
2181 for (auto * child : std::as_const(*children))
2182 {
2183 QString title = child->GetText();
2184 int id = child->getPosition();
2185 idTitle.insert(id, title);
2186 }
2187
2188 if (m_d->m_type == DLG_TREE)
2189 {
2191 MythGenericTree *new_node = dlgParent->getChildAt(idTitle.key(string));
2192 if (new_node)
2193 {
2196 }
2197 }
2198 else
2199 {
2200 m_videoButtonList->SetItemCurrent(idTitle.key(string));
2201 }
2202}
2203
2209{
2211
2212 QStringList childList;
2213 QList<MythGenericTree*> *children = nullptr;
2214 if (parent && m_d->m_type == DLG_TREE)
2215 children = parent->getAllChildren();
2216 else
2217 children = m_d->m_currentNode->getAllChildren();
2218
2219 for (auto * child : std::as_const(*children))
2220 {
2221 childList << child->GetText();
2222 }
2223
2224 MythScreenStack *popupStack =
2225 GetMythMainWindow()->GetStack("popup stack");
2226 auto *searchDialog = new MythUISearchDialog(popupStack,
2227 tr("Video Search"), childList, false, "");
2228
2229 if (searchDialog->Create())
2230 {
2231 connect(searchDialog, &MythUISearchDialog::haveResult,
2233
2234 popupStack->AddScreen(searchDialog);
2235 }
2236 else
2237 {
2238 delete searchDialog;
2239 }
2240}
2241
2247{
2248 bool handled = false;
2249
2251 {
2253 if (lparent)
2254 {
2255 SetCurrentNode(lparent);
2256
2257 handled = true;
2258 }
2259 }
2260
2261 loadData();
2262
2263 return handled;
2264}
2265
2271{
2272 if (!node)
2273 return;
2274
2275 m_d->m_currentNode = node;
2276}
2277
2283{
2285 MythUIButtonList *currentList = ci ? ci->parent() : nullptr;
2286
2287 if (!currentList)
2288 return;
2289
2290 CheckedSet(m_positionText, tr("%1 of %2")
2291 .arg(currentList->IsEmpty() ? 0 : currentList->GetCurrentPos() + 1)
2292 .arg(currentList->GetCount()));
2293}
2294
2300{
2301 if (!item)
2302 return;
2303
2304 VideoMetadata *metadata = GetMetadata(item);
2305 if (!metadata)
2306 return;
2307
2308 CopyPlaybackStateToUI(m_d->m_playbackState, metadata, item, nullptr);
2309}
2310
2316{
2317 if (!item)
2318 return;
2319
2320 MythUIButtonList *currentList = item->parent();
2321
2322 if (!currentList)
2323 return;
2324
2325 VideoMetadata *metadata = GetMetadata(item);
2326
2328
2329 if (!node)
2330 return;
2331
2332 if (metadata)
2333 {
2334 InfoMap metadataMap;
2335 metadata->toMap(metadataMap);
2336 SetTextFromMap(metadataMap);
2337 }
2338 else
2339 {
2340 InfoMap metadataMap;
2341 ClearMap(metadataMap);
2342 SetTextFromMap(metadataMap);
2343 }
2344
2345 ScreenCopyDest dest(this);
2346 CopyMetadataToUI(metadata, dest);
2347 CopyPlaybackStateToUI(m_d->m_playbackState, metadata, item, m_d->m_currentNode ? this : nullptr);
2348
2349 if (node->getInt() == kSubFolder && !metadata)
2350 {
2351 QString cover = GetFirstImage(node, "Coverart");
2352 QString fanart = GetFirstImage(node, "Fanart");
2353 QString banner = GetFirstImage(node, "Banners");
2354 QString screenshot = GetFirstImage(node, "Screenshots");
2355 CheckedSet(m_coverImage, cover);
2356 CheckedSet(m_fanart, fanart);
2357 CheckedSet(m_banner, banner);
2358 CheckedSet(m_screenshot, screenshot);
2359 }
2360
2361 if (!metadata)
2362 CheckedSet(m_titleText, item->GetText());
2364
2365 if (m_d->m_currentNode)
2366 {
2368 CheckedSet(this, "foldername", m_d->m_currentNode->GetText());
2369 }
2370
2371 if (node && node->getInt() == kSubFolder)
2372 CheckedSet(this, "childcount",
2373 QString("%1").arg(node->visibleChildCount()));
2374
2375 if (node)
2376 node->becomeSelectedChild();
2377}
2378
2388{
2389 if (!gCoreContext->GetBoolSetting("AutomaticSetWatched", false))
2390 return;
2391
2392 if (!item)
2393 return;
2394
2395 VideoMetadata *metadata = GetMetadata(item);
2396 if (!metadata)
2397 return;
2398
2399 auto metadataNew = VideoMetadataListManager::loadOneFromDatabase(metadata->GetID());
2400 if (metadata->GetWatched() != metadataNew->GetWatched())
2401 {
2402 metadata->SetWatched(metadataNew->GetWatched());
2403 item->DisplayState(WatchedToState(metadata->GetWatched()), "watchedstate");
2404 }
2405}
2406
2412{
2414 QString label;
2415
2416 if (metadata)
2417 {
2418 if (!metadata->GetSubtitle().isEmpty())
2419 {
2420 label = tr("Video Options\n%1\n%2").arg(metadata->GetTitle(),
2421 metadata->GetSubtitle());
2422 }
2423 else
2424 {
2425 label = tr("Video Options\n%1").arg(metadata->GetTitle());
2426 }
2427 }
2428 else
2429 {
2430 label = tr("Video Options");
2431 }
2432
2433 auto *menu = new MythMenu(label, this, "actions");
2434
2437 if (metadata)
2438 {
2439 if (!metadata->GetTrailer().isEmpty() ||
2440 gCoreContext->GetBoolSetting("mythvideo.TrailersRandomEnabled", false) ||
2442 menu->AddItem(tr("Play..."), nullptr, CreatePlayMenu());
2443 else
2444 menu->AddItem(tr("Play"), &VideoDialog::playVideo);
2445 if (metadata->GetWatched())
2446 menu->AddItem(tr("Mark as Unwatched"), &VideoDialog::ToggleWatched);
2447 else
2448 menu->AddItem(tr("Mark as Watched"), &VideoDialog::ToggleWatched);
2449 menu->AddItem(tr("Video Info"), nullptr, CreateInfoMenu());
2450 if (!m_d->m_notifications.contains(metadata->GetHash()))
2451 {
2452 menu->AddItem(tr("Change Video Details"), nullptr, CreateManageMenu());
2453 }
2454 menu->AddItem(tr("Delete"), &VideoDialog::RemoveVideo);
2455 }
2456 else if (node && node->getInt() != kUpFolder)
2457 {
2458 menu->AddItem(tr("Play Folder"), &VideoDialog::playFolder);
2459 }
2460
2461
2462 m_menuPopup = new MythDialogBox(menu, m_popupStack, "videomenupopup");
2463
2464 if (m_menuPopup->Create())
2465 {
2468 }
2469 else
2470 {
2471 delete m_menuPopup;
2472 }
2473}
2474
2481{
2483 QString label;
2484
2485 if (metadata)
2486 label = tr("Playback Options\n%1").arg(metadata->GetTitle());
2487 else
2488 return nullptr;
2489
2490 auto *menu = new MythMenu(label, this, "actions");
2491
2492 menu->AddItem(tr("Play"), &VideoDialog::playVideo);
2493
2495 {
2496 menu->AddItem(tr("Play in Alternate Player"), &VideoDialog::playVideoAlt);
2497 }
2498
2499 if (gCoreContext->GetBoolSetting("mythvideo.TrailersRandomEnabled", false))
2500 {
2501 menu->AddItem(tr("Play With Trailers"), &VideoDialog::playVideoWithTrailers);
2502 }
2503
2504 QString trailerFile = metadata->GetTrailer();
2505 if (QFile::exists(trailerFile) ||
2506 (!metadata->GetHost().isEmpty() && !trailerFile.isEmpty()))
2507 {
2508 menu->AddItem(tr("Play Trailer"), &VideoDialog::playTrailer);
2509 }
2510
2511 return menu;
2512}
2513
2519{
2520 QString label = tr("Video Display Menu");
2521
2522 auto *menu = new MythMenu(label, this, "display");
2523
2524 menu->AddItem(tr("Scan For Changes"), &VideoDialog::doVideoScan);
2525 menu->AddItem(tr("Retrieve All Details"), qOverload<>(&VideoDialog::VideoAutoSearch));
2526 menu->AddItem(tr("Filter Display"), &VideoDialog::ChangeFilter);
2527 menu->AddItem(tr("Browse By..."), nullptr, CreateMetadataBrowseMenu());
2528 menu->AddItem(tr("Change View"), nullptr, CreateViewMenu());
2529 menu->AddItem(tr("Settings"), nullptr, CreateSettingsMenu());
2530
2531 m_menuPopup = new MythDialogBox(menu, m_popupStack, "videomenupopup");
2532
2533 if (m_menuPopup->Create())
2534 {
2537 }
2538 else
2539 {
2540 delete m_menuPopup;
2541 }
2542}
2543
2544// Switch from the display menu to the actions menu on second
2545// menu press
2546
2547void VideoDialog::popupClosed(const QString& which, int result)
2548{
2549 m_menuPopup = nullptr;
2550
2551 if (result == -2)
2552 {
2553 if (which == "display")
2554 VideoMenu();
2555 }
2556}
2557
2563{
2564 QString label = tr("Change View");
2565
2566 auto *menu = new MythMenu(label, this, "view");
2567
2568 if (!(m_d->m_type & DLG_BROWSER))
2569 menu->AddItem(tr("Switch to Browse View"), &VideoDialog::SwitchBrowse);
2570
2571 if (!(m_d->m_type & DLG_GALLERY))
2572 menu->AddItem(tr("Switch to Gallery View"), &VideoDialog::SwitchGallery);
2573
2574 if (!(m_d->m_type & DLG_TREE))
2575 menu->AddItem(tr("Switch to List View"), &VideoDialog::SwitchTree);
2576
2577 if (!(m_d->m_type & DLG_MANAGER))
2578 menu->AddItem(tr("Switch to Manage View"), &VideoDialog::SwitchManager);
2579
2580 if (m_d->m_isFlatList)
2581 menu->AddItem(tr("Show Directory Structure"), &VideoDialog::ToggleFlatView);
2582 else
2583 menu->AddItem(tr("Hide Directory Structure"), &VideoDialog::ToggleFlatView);
2584
2585 if (m_d->m_isFileBrowser)
2586 menu->AddItem(tr("Browse Library (recommended)"), &VideoDialog::ToggleBrowseMode);
2587 else
2588 menu->AddItem(tr("Browse Filesystem (slow)"), &VideoDialog::ToggleBrowseMode);
2589
2590
2591 return menu;
2592}
2593
2599{
2600 QString label = tr("Video Settings");
2601
2602 auto *menu = new MythMenu(label, this, "settings");
2603
2604 menu->AddItem(tr("Player Settings"), &VideoDialog::ShowPlayerSettings);
2605 menu->AddItem(tr("Metadata Settings"), &VideoDialog::ShowMetadataSettings);
2606 menu->AddItem(tr("File Type Settings"), &VideoDialog::ShowExtensionSettings);
2607
2608 return menu;
2609}
2610
2616{
2617 auto *ps = new PlayerSettings(m_mainStack, "player settings");
2618
2619 if (ps->Create())
2621 else
2622 delete ps;
2623}
2624
2630{
2631 auto *ms = new MetadataSettings(m_mainStack, "metadata settings");
2632
2633 if (ms->Create())
2635 else
2636 delete ms;
2637}
2638
2644{
2645 auto *fa = new FileAssocDialog(m_mainStack, "fa dialog");
2646
2647 if (fa->Create())
2649 else
2650 delete fa;
2651}
2652
2658{
2659 QString label = tr("Browse By");
2660
2661 auto *menu = new MythMenu(label, this, "metadata");
2662
2663 if (m_d->m_groupType != BRS_CAST)
2664 menu->AddItem(tr("Cast"), &VideoDialog::SwitchVideoCastGroup);
2665
2667 menu->AddItem(tr("Category"), &VideoDialog::SwitchVideoCategoryGroup);
2668
2670 menu->AddItem(tr("Date Added"), &VideoDialog::SwitchVideoInsertDateGroup);
2671
2673 menu->AddItem(tr("Director"), &VideoDialog::SwitchVideoDirectorGroup);
2674
2675 if (m_d->m_groupType != BRS_STUDIO)
2676 menu->AddItem(tr("Studio"), &VideoDialog::SwitchVideoStudioGroup);
2677
2678 if (m_d->m_groupType != BRS_FOLDER)
2679 menu->AddItem(tr("Folder"), &VideoDialog::SwitchVideoFolderGroup);
2680
2681 if (m_d->m_groupType != BRS_GENRE)
2682 menu->AddItem(tr("Genre"), &VideoDialog::SwitchVideoGenreGroup);
2683
2684 if (m_d->m_groupType != BRS_TVMOVIE)
2685 menu->AddItem(tr("TV/Movies"), &VideoDialog::SwitchVideoTVMovieGroup);
2686
2688 menu->AddItem(tr("User Rating"), &VideoDialog::SwitchVideoUserRatingGroup);
2689
2690 if (m_d->m_groupType != BRS_YEAR)
2691 menu->AddItem(tr("Year"), &VideoDialog::SwitchVideoYearGroup);
2692
2693 return menu;
2694}
2695
2701{
2702 QString label = tr("Video Info");
2703
2704 auto *menu = new MythMenu(label, this, "info");
2705
2707 menu->AddItem(tr("View Details"), &VideoDialog::DoItemDetailShow2);
2708
2709 menu->AddItem(tr("View Full Plot"), &VideoDialog::ViewPlot);
2710
2712 if (metadata)
2713 {
2714 if (!metadata->GetCast().empty())
2715 menu->AddItem(tr("View Cast"), &VideoDialog::ShowCastDialog);
2716 if (!metadata->GetHomepage().isEmpty())
2717 menu->AddItem(tr("View Homepage"), &VideoDialog::ShowHomepage);
2718 }
2719
2720 return menu;
2721}
2722
2728{
2729 QString label = tr("Manage Video Details");
2730
2731 auto *menu = new MythMenu(label, this, "manage");
2732
2734
2735 menu->AddItem(tr("Edit Details"), &VideoDialog::EditMetadata);
2736 menu->AddItem(tr("Retrieve Details"), qOverload<>(&VideoDialog::VideoSearch));
2737 if (metadata->GetProcessed())
2738 menu->AddItem(tr("Allow Updates"), &VideoDialog::ToggleProcess);
2739 else
2740 menu->AddItem(tr("Disable Updates"), &VideoDialog::ToggleProcess);
2741 menu->AddItem(tr("Reset Details"), &VideoDialog::ResetMetadata);
2742
2743 return menu;
2744}
2745
2747{
2749 if (metadata)
2750 {
2751 metadata->SetProcessed(!metadata->GetProcessed());
2752 metadata->UpdateDatabase();
2753
2754 refreshData();
2755 }
2756}
2757
2763{
2765 gCoreContext->SaveSetting("VideoDialogNoDB",
2766 QString("%1").arg((int)m_d->m_isFileBrowser));
2767 reloadData();
2768}
2769
2775{
2777 gCoreContext->SaveSetting(QString("mythvideo.folder_view_%1").arg(m_d->m_type),
2778 QString("%1").arg((int)m_d->m_isFlatList));
2779 // TODO: This forces a complete tree rebuild, this is SLOW and shouldn't
2780 // be necessary since MythGenericTree can do a flat view without a rebuild,
2781 // I just don't want to re-write VideoList just now
2782 reloadData();
2783}
2784
2790{
2791 SetCurrentNode(node);
2792 loadData();
2793}
2794
2800{
2801 QStringList route = node->getRouteByString();
2802 if (m_d->m_videoList && m_d->m_videoList->refreshNode(node))
2803 reloadData();
2805}
2806
2812{
2814 int nodeInt = node->getInt();
2815
2816 switch (nodeInt)
2817 {
2818 case kDynamicSubFolder:
2820 break;
2821 case kSubFolder:
2822 handleDirSelect(node);
2823 break;
2824 case kUpFolder:
2825 goBack();
2826 break;
2827 default:
2828 {
2829 bool doPlay = true;
2830 if (m_d->m_type == DLG_GALLERY)
2831 {
2832 doPlay = !DoItemDetailShow();
2833 }
2834
2835 if (doPlay)
2836 playVideo();
2837 }
2838 };
2839}
2840
2846{
2848}
2849
2855{
2857}
2858
2864{
2866}
2867
2873{
2875}
2876
2882{
2884}
2885
2891{
2893}
2894
2900{
2902}
2903
2909{
2911}
2912
2918{
2920}
2921
2927{
2929}
2930
2936{
2938}
2939
2945{
2947}
2948
2954{
2956}
2957
2963{
2965}
2966
2972{
2973 m_d->m_switchingLayout = true;
2974
2975 // save current position so it can be restored after the switch
2976 SavePosition();
2977
2978 auto *mythvideo =
2979 new VideoDialog(GetMythMainWindow()->GetMainStack(), "mythvideo",
2980 m_d->m_videoList, type, browse);
2981
2982 if (mythvideo->Create())
2983 {
2984 gCoreContext->SaveSetting("Default MythVideo View", type);
2985 gCoreContext->SaveSetting("mythvideo.db_group_type", browse);
2986 MythScreenStack *screenStack = GetScreenStack();
2987 screenStack->AddScreen(mythvideo);
2988 screenStack->PopScreen(this, false, false);
2989 deleteLater();
2990 }
2991 else
2992 {
2993 ShowOkPopup(tr("An error occurred when switching views."));
2994 }
2995}
2996
3002{
3004
3005 auto *plotdialog = new PlotDialog(m_popupStack, metadata);
3006
3007 if (plotdialog->Create())
3008 m_popupStack->AddScreen(plotdialog);
3009}
3010
3016{
3018
3019 if (metadata)
3020 {
3022 auto *idp = new ItemDetailPopup(mainStack, metadata,
3024
3025 if (idp->Create())
3026 {
3027 mainStack->AddScreen(idp);
3028 return true;
3029 }
3030 }
3031
3032 return false;
3033}
3034
3040{
3042
3043 auto *castdialog = new CastDialog(m_popupStack, metadata);
3044
3045 if (castdialog->Create())
3046 m_popupStack->AddScreen(castdialog);
3047}
3048
3050{
3052
3053 if (!metadata)
3054 return;
3055
3056 QString url = metadata->GetHomepage();
3057
3058 if (url.isEmpty())
3059 return;
3060
3061 QString browser = gCoreContext->GetSetting("WebBrowserCommand", "");
3062 QString zoom = gCoreContext->GetSetting("WebBrowserZoomLevel", "1.0");
3063
3064 if (browser.isEmpty())
3065 {
3066 ShowOkPopup(tr("No browser command set! MythVideo needs MythBrowser "
3067 "installed to display the homepage."));
3068 return;
3069 }
3070
3071 if (browser.toLower() == "internal")
3072 {
3073 GetMythMainWindow()->HandleMedia("WebBrowser", url);
3074 return;
3075 }
3076
3077 QString cmd = browser;
3078 cmd.replace("%ZOOM%", zoom);
3079 cmd.replace("%URL%", url);
3080 cmd.replace('\'', "%27");
3081 cmd.replace("&","\\&");
3082 cmd.replace(";","\\;");
3083
3084 GetMythMainWindow()->AllowInput(false);
3087}
3088
3094{
3096 if (metadata && m_d->m_videoList)
3098}
3099
3105{
3107 if (metadata && m_d->m_videoList)
3108 PlayVideo(metadata->GetFilename(), m_d->m_videoList->getListCache(), true);
3109}
3110
3116{
3117 const int WATCHED_WATERMARK = 10000; // Play less then this milisec and the chain of
3118 // videos will not be followed when
3119 // playing.
3120 QElapsedTimer playing_time;
3121
3124 int list_count = 0;
3125
3126 if (node && !(node->getInt() >= 0))
3127 list_count = node->childCount();
3128 else
3129 return;
3130
3131 if (list_count > 0)
3132 {
3133 bool video_started = false;
3134 int i = 0;
3135 while (i < list_count &&
3136 (!video_started || playing_time.hasExpired(WATCHED_WATERMARK)))
3137 {
3138 MythGenericTree *subnode = node->getChildAt(i);
3139 if (subnode)
3140 {
3141 VideoMetadata *metadata = GetMetadataPtrFromNode(subnode);
3142 if (metadata && m_d->m_videoList)
3143 {
3145 playing_time.start();
3146 video_started = true;
3147 }
3148 }
3149 i++;
3150 }
3151 }
3152}
3153
3154namespace
3155{
3157 {
3158 explicit SimpleCollect(QStringList &fileList) : m_fileList(fileList) {}
3159
3160 DirectoryHandler *newDir([[maybe_unused]] const QString &dirName,
3161 [[maybe_unused]] const QString &fqDirName) override // DirectoryHandler
3162 {
3163 return this;
3164 }
3165
3166 void handleFile([[maybe_unused]] const QString &fileName,
3167 const QString &fqFileName,
3168 [[maybe_unused]] const QString &extension,
3169 [[maybe_unused]] const QString &host) override // DirectoryHandler
3170 {
3171 m_fileList.push_back(fqFileName);
3172 }
3173
3174 private:
3175 QStringList &m_fileList;
3176 };
3177
3178 QStringList GetTrailersInDirectory(const QString &startDir)
3179 {
3182 .getExtensionIgnoreList(extensions);
3183 QStringList ret;
3184 SimpleCollect sc(ret);
3185
3186 (void) ScanVideoDirectory(startDir, &sc, extensions, false);
3187 return ret;
3188 }
3189}
3190
3196{
3198 if (!metadata) return;
3199
3201 GetSetting("mythvideo.TrailersDir"));
3202
3203 if (trailers.isEmpty())
3204 return;
3205
3206 const int trailersToPlay =
3207 gCoreContext->GetNumSetting("mythvideo.TrailersRandomCount");
3208
3209 int i = 0;
3210 while (!trailers.isEmpty() && i < trailersToPlay)
3211 {
3212 ++i;
3213 QString trailer = trailers.takeAt(MythRandom(0, trailers.size() - 1));
3214
3215 LOG(VB_GENERAL, LOG_DEBUG,
3216 QString("Random trailer to play will be: %1").arg(trailer));
3217
3219 }
3220
3222}
3223
3229{
3231 if (!metadata) return;
3232 QString url;
3233
3234 if (metadata->IsHostSet() && !metadata->GetTrailer().startsWith("/"))
3235 {
3236 url = StorageGroup::generate_file_url("Trailers", metadata->GetHost(),
3237 metadata->GetTrailer());
3238 }
3239 else
3240 {
3241 url = metadata->GetTrailer();
3242 }
3243
3245}
3246
3252{
3253 m_d->m_parentalLevel.SetLevel(level);
3254}
3255
3261{
3263 .GetLevel() + amount).GetLevel());
3264}
3265
3271{
3272 MythScreenStack *mainStack = GetScreenStack();
3273
3274 auto *filterdialog = new VideoFilterDialog(mainStack,
3275 "videodialogfilters", m_d->m_videoList.get());
3276
3277 if (filterdialog->Create())
3278 mainStack->AddScreen(filterdialog);
3279
3280 connect(filterdialog, &VideoFilterDialog::filterChanged, this, &VideoDialog::reloadData);
3281}
3282
3288{
3289 VideoMetadata *metadata = nullptr;
3290
3291 if (item)
3292 {
3294 if (node)
3295 {
3296 int nodeInt = node->getInt();
3297
3298 if (nodeInt >= 0)
3299 metadata = GetMetadataPtrFromNode(node);
3300 }
3301 }
3302
3303 return metadata;
3304}
3305
3306void VideoDialog::customEvent(QEvent *levent)
3307{
3308 if (levent->type() == MetadataFactoryMultiResult::kEventType)
3309 {
3310 auto *mfmr = dynamic_cast<MetadataFactoryMultiResult*>(levent);
3311
3312 if (!mfmr)
3313 return;
3314
3315 MetadataLookupList list = mfmr->m_results;
3316
3317 if (list.count() > 1)
3318 {
3319 auto *metadata = list[0]->GetData().value<VideoMetadata *>();
3320 dismissFetchDialog(metadata, true);
3321 auto *resultsdialog = new MetadataResultsDialog(m_popupStack, list);
3322
3323 connect(resultsdialog, &MetadataResultsDialog::haveResult,
3325 Qt::QueuedConnection);
3326
3327 if (resultsdialog->Create())
3328 m_popupStack->AddScreen(resultsdialog);
3329 }
3330 }
3331 else if (levent->type() == MetadataFactorySingleResult::kEventType)
3332 {
3333 auto *mfsr = dynamic_cast<MetadataFactorySingleResult*>(levent);
3334
3335 if (!mfsr)
3336 return;
3337
3338 MetadataLookup *lookup = mfsr->m_result;
3339
3340 if (!lookup)
3341 return;
3342
3343 OnVideoSearchDone(lookup);
3344 }
3345 else if (levent->type() == MetadataFactoryNoResult::kEventType)
3346 {
3347 auto *mfnr = dynamic_cast<MetadataFactoryNoResult*>(levent);
3348
3349 if (!mfnr)
3350 return;
3351
3352 MetadataLookup *lookup = mfnr->m_result;
3353
3354 if (!lookup)
3355 return;
3356
3357 auto *metadata = lookup->GetData().value<VideoMetadata *>();
3358 if (metadata)
3359 {
3360 dismissFetchDialog(metadata, false);
3361 metadata->SetProcessed(true);
3362 metadata->UpdateDatabase();
3363 }
3364 LOG(VB_GENERAL, LOG_INFO,
3365 QString("No results found for %1 %2 %3").arg(lookup->GetTitle())
3366 .arg(lookup->GetSeason()).arg(lookup->GetEpisode()));
3367 }
3368 else if (levent->type() == DialogCompletionEvent::kEventType)
3369 {
3370 auto *dce = dynamic_cast<DialogCompletionEvent *>(levent);
3371 if (dce != nullptr)
3372 {
3373 QString id = dce->GetId();
3374
3375 if (id == "scanprompt")
3376 {
3377 int result = dce->GetResult();
3378 if (result == 1)
3379 doVideoScan();
3380 }
3381 else
3382 {
3383 m_menuPopup = nullptr;
3384 }
3385 }
3386 else
3387 {
3388 m_menuPopup = nullptr;
3389 }
3390 }
3391 else if (levent->type() == ImageDLFailureEvent::kEventType)
3392 {
3393 MythErrorNotification n(tr("Failed to retrieve image(s)"),
3394 sLocation,
3395 tr("Check logs"));
3397 }
3398}
3399
3401{
3402 // The metadata has some cover file set
3403 dismissFetchDialog(metadata, true);
3404
3405 metadata->SetProcessed(true);
3406 metadata->UpdateDatabase();
3407
3408 MythUIButtonListItem *item = GetItemByMetadata(metadata);
3409 if (item != nullptr)
3410 UpdateItem(item);
3411}
3412
3414{
3416 {
3418 }
3419
3421}
3422
3424{
3426 {
3428 }
3429
3430 QMap<int, int> idPosition;
3431
3432 QList<MythGenericTree*> *children = m_d->m_currentNode->getAllChildren();
3433
3434 for (auto * child : std::as_const(*children))
3435 {
3436 int nodeInt = child->getInt();
3437 if (nodeInt != kSubFolder && nodeInt != kUpFolder)
3438 {
3439 VideoMetadata *listmeta =
3441 if (listmeta)
3442 {
3443 int position = child->getPosition();
3444 int id = listmeta->GetID();
3445 idPosition.insert(id, position);
3446 }
3447 }
3448 }
3449
3450 return m_videoButtonList->GetItemAt(idPosition.value(metadata->GetID()));
3451}
3452
3454 bool automode)
3455{
3456 if (!node)
3458
3459 if (!node)
3460 return;
3461
3462 VideoMetadata *metadata = GetMetadataPtrFromNode(node);
3463
3464 if (!metadata)
3465 return;
3466
3467 m_metadataFactory->Lookup(metadata, automode, true);
3468
3469 if (!automode)
3470 {
3471 createFetchDialog(metadata);
3472 }
3473}
3474
3476{
3477 if (!node)
3478 node = m_d->m_rootNode;
3479 using MGTreeChildList = QList<MythGenericTree *>;
3480 MGTreeChildList *lchildren = node->getAllChildren();
3481
3482 LOG(VB_GENERAL, LOG_DEBUG,
3483 QString("Fetching details in %1").arg(node->GetText()));
3484
3485 for (auto * child : std::as_const(*lchildren))
3486 {
3487 if ((child->getInt() == kSubFolder) ||
3488 (child->getInt() == kUpFolder))
3489 VideoAutoSearch(child);
3490 else
3491 {
3492 VideoMetadata *metadata = GetMetadataPtrFromNode(child);
3493
3494 if (!metadata)
3495 continue;
3496
3497 if (!metadata->GetProcessed())
3498 VideoSearch(child, true);
3499 }
3500 }
3501}
3502
3504{
3506 if (!item)
3507 return;
3508
3509 VideoMetadata *metadata = GetMetadata(item);
3510 if (metadata)
3511 {
3512 metadata->SetWatched(!metadata->GetWatched());
3513 metadata->UpdateDatabase();
3514 item->DisplayState(WatchedToState(metadata->GetWatched()),
3515 "watchedstate");
3516 }
3517}
3518
3520{
3521 if (!lookup)
3522 return;
3523
3524 if(!lookup->GetInetref().isEmpty() && lookup->GetInetref() != "00000000")
3525 {
3526 LOG(VB_GENERAL, LOG_INFO, LOC_MML +
3527 QString("Selected Item: Type: %1%2 : Subtype: %3%4%5 : InetRef: %6")
3528 .arg(lookup->GetType() == kMetadataVideo ? "Video" : "",
3529 lookup->GetType() == kMetadataRecording ? "Recording" : "",
3530 lookup->GetSubtype() == kProbableMovie ? "Movie" : "",
3531 lookup->GetSubtype() == kProbableTelevision ? "Television" : "",
3532 lookup->GetSubtype() == kUnknownVideo ? "Unknown" : "",
3533 lookup->GetInetref()));
3534
3535 lookup->SetStep(kLookupData);
3536 lookup->IncrRef();
3537 m_metadataFactory->Lookup(lookup);
3538 }
3539 else
3540 {
3541 LOG(VB_GENERAL, LOG_ERR, LOC_MML +
3542 QString("Selected Item has no InetRef Number!"));
3543
3544 OnVideoSearchDone(lookup);
3545 }
3546}
3547
3549{
3551 if (metadata)
3552 {
3553 ParentalLevel curshowlevel = metadata->GetShowLevel();
3554
3555 curshowlevel += amount;
3556
3557 if (curshowlevel.GetLevel() != metadata->GetShowLevel())
3558 {
3559 metadata->SetShowLevel(curshowlevel.GetLevel());
3560 metadata->UpdateDatabase();
3561 refreshData();
3562 }
3563 }
3564}
3565
3567{
3569 if (!metadata)
3570 return;
3571
3572 MythScreenStack *screenStack = GetScreenStack();
3573
3574 auto *md_editor = new EditMetadataDialog(screenStack,
3575 "mythvideoeditmetadata", metadata,
3577
3578 connect(md_editor, &EditMetadataDialog::Finished, this, &VideoDialog::refreshData);
3579
3580 if (md_editor->Create())
3581 screenStack->AddScreen(md_editor);
3582}
3583
3585{
3587
3588 if (!metadata)
3589 return;
3590
3591 QString message = tr("Are you sure you want to permanently delete:\n%1")
3592 .arg(metadata->GetTitle());
3593
3594 auto *confirmdialog = new MythConfirmationDialog(m_popupStack,message);
3595
3596 if (confirmdialog->Create())
3597 m_popupStack->AddScreen(confirmdialog);
3598
3599 connect(confirmdialog, &MythConfirmationDialog::haveResult,
3601}
3602
3604{
3605 if (!dodelete)
3606 return;
3607
3609 MythGenericTree *gtItem = GetNodePtrFromButton(item);
3610
3611 VideoMetadata *metadata = GetMetadata(item);
3612
3613 if (!metadata)
3614 return;
3615
3616 if (m_d->m_videoList && m_d->m_videoList->Delete(metadata->GetID()))
3617 {
3619 m_videoButtonTree->RemoveItem(item, false); // FIXME Segfault when true
3620 else
3622
3623 MythGenericTree *parent = gtItem->getParent();
3624 parent->deleteNode(gtItem);
3625 }
3626 else
3627 {
3628 QString message = tr("Failed to delete file");
3629
3630 auto *confirmdialog = new MythConfirmationDialog(m_popupStack,message,
3631 false);
3632
3633 if (confirmdialog->Create())
3634 m_popupStack->AddScreen(confirmdialog);
3635 }
3636}
3637
3639{
3641 VideoMetadata *metadata = GetMetadata(item);
3642
3643 if (metadata)
3644 {
3645 metadata->Reset();
3646 metadata->UpdateDatabase();
3647 UpdateItem(item);
3648 }
3649}
3650
3652{
3653 if (!metadata)
3654 return;
3655
3656 QStringList cover_dirs;
3657 cover_dirs += m_d->m_artDir;
3658
3659 QString cover_file;
3660 QString inetref = metadata->GetInetRef();
3661 QString filename = metadata->GetFilename();
3662 QString title = metadata->GetTitle();
3663 int season = metadata->GetSeason();
3664 QString host = metadata->GetHost();
3665 int episode = metadata->GetEpisode();
3666
3667 if (metadata->GetCoverFile().isEmpty() ||
3668 IsDefaultCoverFile(metadata->GetCoverFile()))
3669 {
3670 if (GetLocalVideoImage(inetref, filename,
3671 cover_dirs, cover_file, title,
3672 season, host, "Coverart", episode))
3673 {
3674 metadata->SetCoverFile(cover_file);
3675 OnVideoImageSetDone(metadata);
3676 }
3677 }
3678
3679 QStringList fanart_dirs;
3680 fanart_dirs += m_d->m_fanDir;
3681
3682 QString fanart_file;
3683
3684 if (metadata->GetFanart().isEmpty())
3685 {
3686 if (GetLocalVideoImage(inetref, filename,
3687 fanart_dirs, fanart_file, title,
3688 season, host, "Fanart", episode))
3689 {
3690 metadata->SetFanart(fanart_file);
3691 OnVideoImageSetDone(metadata);
3692 }
3693 }
3694
3695 QStringList banner_dirs;
3696 banner_dirs += m_d->m_banDir;
3697
3698 QString banner_file;
3699
3700 if (metadata->GetBanner().isEmpty())
3701 {
3702 if (GetLocalVideoImage(inetref, filename,
3703 banner_dirs, banner_file, title,
3704 season, host, "Banners", episode))
3705 {
3706 metadata->SetBanner(banner_file);
3707 OnVideoImageSetDone(metadata);
3708 }
3709 }
3710
3711 QStringList screenshot_dirs;
3712 screenshot_dirs += m_d->m_sshotDir;
3713
3714 QString screenshot_file;
3715
3716 if (metadata->GetScreenshot().isEmpty())
3717 {
3718 if (GetLocalVideoImage(inetref, filename,
3719 screenshot_dirs, screenshot_file, title,
3720 season, host, "Screenshots", episode,
3721 true))
3722 {
3723 metadata->SetScreenshot(screenshot_file);
3724 OnVideoImageSetDone(metadata);
3725 }
3726 }
3727}
3728
3730{
3731 if (!lookup)
3732 return;
3733
3734 auto *metadata = lookup->GetData().value<VideoMetadata *>();
3735
3736 if (!metadata)
3737 return;
3738
3739 dismissFetchDialog(metadata, true);
3740 metadata->SetTitle(lookup->GetTitle());
3741 metadata->SetSubtitle(lookup->GetSubtitle());
3742
3743 if (metadata->GetTagline().isEmpty())
3744 metadata->SetTagline(lookup->GetTagline());
3745 if (metadata->GetYear() == 1895 || metadata->GetYear() == 0)
3746 metadata->SetYear(lookup->GetYear());
3747 if (metadata->GetReleaseDate() == QDate())
3748 metadata->SetReleaseDate(lookup->GetReleaseDate());
3749 if (metadata->GetDirector() == VIDEO_DIRECTOR_UNKNOWN ||
3750 metadata->GetDirector().isEmpty())
3751 {
3752 QList<PersonInfo> director = lookup->GetPeople(kPersonDirector);
3753 if (director.count() > 0)
3754 metadata->SetDirector(director.takeFirst().name);
3755 }
3756 if (metadata->GetStudio().isEmpty())
3757 {
3758 QStringList studios = lookup->GetStudios();
3759 if (studios.count() > 0)
3760 metadata->SetStudio(studios.takeFirst());
3761 }
3762 if (metadata->GetPlot() == VIDEO_PLOT_DEFAULT ||
3763 metadata->GetPlot().isEmpty())
3764 metadata->SetPlot(lookup->GetDescription());
3765 if (metadata->GetUserRating() == 0)
3766 metadata->SetUserRating(lookup->GetUserRating());
3767 if (metadata->GetRating() == VIDEO_RATING_DEFAULT)
3768 metadata->SetRating(lookup->GetCertification());
3769 if (metadata->GetLength() == 0min)
3770 metadata->SetLength(lookup->GetRuntime());
3771 if (metadata->GetSeason() == 0)
3772 metadata->SetSeason(lookup->GetSeason());
3773 if (metadata->GetEpisode() == 0)
3774 metadata->SetEpisode(lookup->GetEpisode());
3775 if (metadata->GetHomepage().isEmpty())
3776 metadata->SetHomepage(lookup->GetHomepage());
3777
3778 metadata->SetInetRef(lookup->GetInetref());
3779
3781
3782 // Cast
3783 QList<PersonInfo> actors = lookup->GetPeople(kPersonActor);
3784 QList<PersonInfo> gueststars = lookup->GetPeople(kPersonGuestStar);
3785
3786 for (const auto & name : std::as_const(gueststars))
3787 actors.append(name);
3788
3790 QStringList cl;
3791
3792 for (const auto & person : std::as_const(actors))
3793 cl.append(person.name);
3794
3795 for (const auto & name : std::as_const(cl))
3796 {
3797 QString cn = name.trimmed();
3798 if (!cn.isEmpty())
3799 {
3800 cast.emplace_back(-1, cn);
3801 }
3802 }
3803
3804 metadata->SetCast(cast);
3805
3806 // Genres
3807 VideoMetadata::genre_list video_genres;
3808 QStringList genres = lookup->GetCategories();
3809
3810 for (const auto & name : std::as_const(genres))
3811 {
3812 QString genre_name = name.trimmed();
3813 if (!genre_name.isEmpty())
3814 {
3815 video_genres.emplace_back(-1, genre_name);
3816 }
3817 }
3818
3819 metadata->SetGenres(video_genres);
3820
3821 // Countries
3822 VideoMetadata::country_list video_countries;
3823 QStringList countries = lookup->GetCountries();
3824
3825 for (const auto & name : std::as_const(countries))
3826 {
3827 QString country_name = name.trimmed();
3828 if (!country_name.isEmpty())
3829 {
3830 video_countries.emplace_back(-1, country_name);
3831 }
3832 }
3833
3834 metadata->SetCountries(video_countries);
3835 metadata->SetProcessed(true);
3836
3837 metadata->UpdateDatabase();
3838
3839 MythUIButtonListItem *item = GetItemByMetadata(metadata);
3840 if (item != nullptr)
3841 UpdateItem(item);
3842
3843 StartVideoImageSet(metadata);
3844}
3845
3847{
3848 if (!m_d->m_scanner)
3849 m_d->m_scanner = new VideoScanner();
3852}
3853
3855{
3856 QString message = tr("There are no videos in the database, would you like "
3857 "to scan your video directories now?");
3858 auto *dialog = new MythConfirmationDialog(m_popupStack, message, true);
3859 dialog->SetReturnEvent(this, "scanprompt");
3860 if (dialog->Create())
3861 m_popupStack->AddScreen(dialog);
3862 else
3863 delete dialog;
3864}
3865
3866#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:2872
void OnParentalChange(int amount)
Definition: videodlg.cpp:3548
void VideoMenu()
Pop up a MythUI "Playback Menu" for MythVideo.
Definition: videodlg.cpp:2411
void playFolder()
Play all the items in the selected folder.
Definition: videodlg.cpp:3115
void ShowMetadataSettings()
Pop up a MythUI Menu for MythVideo Metadata Settings.
Definition: videodlg.cpp:2629
void popupClosed(const QString &which, int result)
Definition: videodlg.cpp:2547
void SwitchVideoUserRatingGroup()
Switch to User Rating browse mode.
Definition: videodlg.cpp:2944
void ShowPlayerSettings()
Pop up a MythUI Menu for MythVideo Player Settings.
Definition: videodlg.cpp:2615
MythUIStateType * m_trailerState
Definition: videodlg.h:214
static VideoMetadata * GetMetadata(MythUIButtonListItem *item)
Retrieve the Database Metadata for a given MythUIButtonListItem.
Definition: videodlg.cpp:3287
void OnVideoImageSetDone(VideoMetadata *metadata)
Definition: videodlg.cpp:3400
bool goBack()
Move one level up in the tree.
Definition: videodlg.cpp:2246
MythUIImage * m_screenshot
Definition: videodlg.h:210
void ResetMetadata()
Definition: videodlg.cpp:3638
void setParentalLevel(ParentalLevel::Level level)
Set the parental level for the library.
Definition: videodlg.cpp:3251
void createBusyDialog(const QString &title)
Create a busy dialog, used during metadata search, etc.
Definition: videodlg.cpp:2070
void SwitchVideoYearGroup()
Switch to Year browse mode.
Definition: videodlg.cpp:2908
void SwitchVideoTVMovieGroup()
Switch to Television/Movie browse mode.
Definition: videodlg.cpp:2962
void handleDynamicDirSelect(MythGenericTree *node)
Request the latest metadata for a folder.
Definition: videodlg.cpp:2799
void fetchVideos()
Build the buttonlist/tree.
Definition: videodlg.cpp:1382
void StartVideoImageSet(VideoMetadata *metadata)
Definition: videodlg.cpp:3651
void SetCurrentNode(MythGenericTree *node)
Switch to a given MythGenericTree node.
Definition: videodlg.cpp:2270
MythMenu * CreateViewMenu()
Create a MythMenu for MythVideo Views.
Definition: videodlg.cpp:2562
void SwitchLayout(DialogType type, BrowseType browse)
Handle a layout or browse mode switch.
Definition: videodlg.cpp:2971
void createFetchDialog(VideoMetadata *metadata)
Create a fetch notification, used during metadata search.
Definition: videodlg.cpp:2088
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:3566
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:1716
void shiftParental(int amount)
Shift the parental level for the library by an integer amount.
Definition: videodlg.cpp:3260
static QString GetCoverImage(MythGenericTree *node)
A "hunt" for cover art to apply for a folder item.
Definition: videodlg.cpp:1484
void doVideoScan()
Definition: videodlg.cpp:3846
void createOkDialog(const QString &title)
Create a MythUI "OK" Dialog.
Definition: videodlg.cpp:2154
bool DoItemDetailShow()
Display the Item Detail Popup.
Definition: videodlg.cpp:3015
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:2890
void refreshData()
Reloads the tree without invalidating the data.
Definition: videodlg.cpp:1129
void ToggleFlatView()
Toggle Flat View.
Definition: videodlg.cpp:2774
virtual void loadData()
load the data used to build the ButtonTree/List for MythVideo.
Definition: videodlg.cpp:1181
void RemoveVideo()
Definition: videodlg.cpp:3584
static QString GetBanner(MythGenericTree *node)
Find the Banner for a given node.
Definition: videodlg.cpp:1889
void SwitchVideoCategoryGroup()
Switch to Category browse mode.
Definition: videodlg.cpp:2899
MythDialogBox * m_menuPopup
Definition: videodlg.h:195
void handleDirSelect(MythGenericTree *node)
Descend into a selected folder.
Definition: videodlg.cpp:2789
void DisplayMenu()
Pop up a MythUI Menu for MythVideo Global Functions.
Definition: videodlg.cpp:2518
void searchComplete(const QString &string)
After using incremental search, move to the selected item.
Definition: videodlg.cpp:2168
void ShowCastDialog()
Display the Cast if the selected item.
Definition: videodlg.cpp:3039
void customEvent(QEvent *levent) override
Definition: videodlg.cpp:3306
void SwitchVideoStudioGroup()
Switch to Studio browse mode.
Definition: videodlg.cpp:2926
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:2657
@ 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:2845
static QString GetScreenshot(MythGenericTree *node)
Find the Screenshot for a given node.
Definition: videodlg.cpp:1849
MythMenu * CreateSettingsMenu()
Create a MythMenu for MythVideo Settings.
Definition: videodlg.cpp:2598
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:2387
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:2881
virtual MythUIButtonListItem * GetItemCurrent()
Definition: videodlg.cpp:3413
MythMenu * CreateManageMenu()
Create a MythMenu for metadata management.
Definition: videodlg.cpp:2727
void ShowHomepage()
Definition: videodlg.cpp:3049
static QString GetFanart(MythGenericTree *node)
Find the Fanart for a given node.
Definition: videodlg.cpp:1924
void UpdateItem(MythUIButtonListItem *item)
Update the visible representation of a MythUIButtonListItem.
Definition: videodlg.cpp:1288
void PromptToScan()
Definition: videodlg.cpp:3854
void UpdateVisible(MythUIButtonListItem *item)
Update playback state for for a given visible ButtonListItem.
Definition: videodlg.cpp:2299
void playTrailer()
Play the trailer associated with the selected item.
Definition: videodlg.cpp:3228
MythUIStateType * m_parentalLevelState
Definition: videodlg.h:215
void SwitchVideoCastGroup()
Switch to Cast browse mode.
Definition: videodlg.cpp:2935
void OnPlaybackStopped()
Definition: videodlg.cpp:916
void playVideoWithTrailers()
Play the selected item w/ a User selectable # of trailers.
Definition: videodlg.cpp:3195
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:3001
void OnVideoSearchDone(MetadataLookup *lookup)
Definition: videodlg.cpp:3729
void UpdatePosition()
Called after the screen is created by MythScreenStack.
Definition: videodlg.cpp:2282
class VideoDialogPrivate * m_d
Definition: videodlg.h:224
void OnVideoSearchListSelection(RefCountHandler< MetadataLookup > lookup)
Definition: videodlg.cpp:3519
void SwitchGallery()
Switch to Gallery View.
Definition: videodlg.cpp:2854
void UpdateText(MythUIButtonListItem *item)
Update the visible text values for a given ButtonListItem.
Definition: videodlg.cpp:2315
void ChangeFilter()
Change the filtering of the library.
Definition: videodlg.cpp:3270
void handleSelect(MythUIButtonListItem *item)
Handle SELECT action for a given MythUIButtonListItem.
Definition: videodlg.cpp:2811
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:2863
void playVideoAlt()
Play the selected item in an alternate player.
Definition: videodlg.cpp:3104
void ToggleProcess()
Definition: videodlg.cpp:2746
void dismissFetchDialog(VideoMetadata *metadata, bool ok)
Definition: videodlg.cpp:2110
simple_ref_ptr< class VideoList > VideoListPtr
Definition: videodlg.h:47
void playVideo()
Play the selected item.
Definition: videodlg.cpp:3093
void searchStart()
Create an incremental search dialog for the current tree level.
Definition: videodlg.cpp:2208
MythMenu * CreatePlayMenu()
Create a "Play Menu" for MythVideo.
Definition: videodlg.cpp:2480
void SwitchVideoInsertDateGroup()
Switch to Insert Date browse mode.
Definition: videodlg.cpp:2953
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:1959
void ToggleBrowseMode()
Toggle the browseable status for the selected item.
Definition: videodlg.cpp:2762
MythMenu * CreateInfoMenu()
Create a MythMenu for Info pertaining to the selected item.
Definition: videodlg.cpp:2700
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:3603
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:3503
virtual MythUIButtonListItem * GetItemByMetadata(VideoMetadata *metadata)
Definition: videodlg.cpp:3423
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:2643
void SwitchVideoDirectorGroup()
Switch to Director browse mode.
Definition: videodlg.cpp:2917
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 guint32 * tmp
Definition: goom_core.cpp:26
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:3178
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:3166
DirectoryHandler * newDir(const QString &dirName, const QString &fqDirName) override
Definition: videodlg.cpp:3160
#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:130
QString ParentalLevelToState(const ParentalLevel &level)
Definition: videoutils.cpp:233
bool IsDefaultFanart(const QString &fanart)
Definition: videoutils.cpp:140
QString WatchedToState(bool watched)
Definition: videoutils.cpp:267
QStringList GetVideoDirsByHost(const QString &host)
Definition: videoutils.cpp:76
QString TrailerToState(const QString &trailerFile)
Definition: videoutils.cpp:257
void CheckedSet(MythUIStateType *uiItem, const QString &value)
Definition: videoutils.cpp:40
bool IsDefaultCoverFile(const QString &coverfile)
Definition: videoutils.cpp:121
bool IsDefaultBanner(const QString &banner)
Definition: videoutils.cpp:135
QStringList GetVideoDirs()
Definition: videoutils.cpp:116