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