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