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