MythTV  master
videoselector.cpp
Go to the documentation of this file.
1 // c
2 #include <cstdlib>
3 #include <unistd.h>
4 
5 // qt
6 #include <QDir>
7 #include <QTimer>
8 
9 // mythtv
10 #include <mythcontext.h>
11 #include <mythdb.h>
12 #include <mythuitext.h>
13 #include <mythuibutton.h>
14 #include <mythuiimage.h>
15 #include <mythuibuttonlist.h>
16 #include <mythmainwindow.h>
17 #include <mythdialogbox.h>
18 #include <metadata/videoutils.h>
19 #include <remotefile.h>
20 
21 // mytharchive
22 #include "videoselector.h"
23 #include "archiveutil.h"
24 
25 using namespace std;
26 
27 VideoSelector::VideoSelector(MythScreenStack *parent, QList<ArchiveItem *> *archiveList)
28  :MythScreenType(parent, "VideoSelector"),
29  m_archiveList(archiveList)
30 {
32  connect(m_parentalLevelChecker, SIGNAL(SigResultReady(bool, ParentalLevel::Level)),
33  this, SLOT(parentalLevelChanged(bool, ParentalLevel::Level)));
34 }
35 
37 {
38  delete m_videoList;
39  while (!m_selectedList.isEmpty())
40  delete m_selectedList.takeFirst();
41  m_selectedList.clear();
43 }
44 
46 {
47  // Load the theme for this screen
48  bool foundtheme = LoadWindowFromXML("mytharchive-ui.xml", "video_selector", this);
49  if (!foundtheme)
50  return false;
51 
52  bool err = false;
53  UIUtilE::Assign(this, m_okButton, "ok_button", &err);
54  UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
55  UIUtilE::Assign(this, m_categorySelector, "category_selector", &err);
56  UIUtilE::Assign(this, m_videoButtonList, "videolist", &err);
57  UIUtilE::Assign(this, m_titleText, "videotitle", &err);
58  UIUtilE::Assign(this, m_plotText, "videoplot", &err);
59  UIUtilE::Assign(this, m_filesizeText, "filesize", &err);
60  UIUtilE::Assign(this, m_coverImage, "cover_image", &err);
61  UIUtilE::Assign(this, m_warningText, "warning_text", &err);
62  UIUtilE::Assign(this, m_plText, "parentallevel_text", &err);
63 
64  if (err)
65  {
66  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'video_selector'");
67  return false;
68  }
69 
70  connect(m_okButton, SIGNAL(Clicked()), SLOT(OKPressed()));
71  connect(m_cancelButton, SIGNAL(Clicked()), SLOT(cancelPressed()));
72 
73  connect(m_categorySelector, SIGNAL(itemSelected(MythUIButtonListItem *)),
75 
76  getVideoList();
77  connect(m_videoButtonList, SIGNAL(itemSelected(MythUIButtonListItem *)),
79  connect(m_videoButtonList, SIGNAL(itemClicked(MythUIButtonListItem *)),
81 
83 
85 
87 
90 
91  return true;
92 }
93 
94 bool VideoSelector::keyPressEvent(QKeyEvent *event)
95 {
96  if (GetFocusWidget()->keyPressEvent(event))
97  return true;
98 
99  QStringList actions;
100  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
101 
102  for (int i = 0; i < actions.size() && !handled; i++)
103  {
104  QString action = actions[i];
105  handled = true;
106 
107  if (action == "MENU")
108  {
109  ShowMenu();
110  }
111  else if (action == "1")
112  {
114  }
115  else if (action == "2")
116  {
118  }
119  else if (action == "3")
120  {
122  }
123  else if (action == "4")
124  {
126  }
127  else
128  handled = false;
129  }
130 
131  if (!handled && MythScreenType::keyPressEvent(event))
132  handled = true;
133 
134  return handled;
135 }
136 
138 {
139  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
140 
141  auto *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
142 
143  if (menuPopup->Create())
144  popupStack->AddScreen(menuPopup);
145 
146  menuPopup->SetReturnEvent(this, "action");
147 
148  menuPopup->AddButton(tr("Clear All"), SLOT(clearAll()));
149  menuPopup->AddButton(tr("Select All"), SLOT(selectAll()));
150 }
151 
153 {
154  while (!m_selectedList.isEmpty())
155  m_selectedList.takeFirst();
156  m_selectedList.clear();
157 
158  for (auto v : *m_videoList)
159  m_selectedList.append(v);
160 
161  updateVideoList();
162 }
163 
165 {
166  while (!m_selectedList.isEmpty())
167  m_selectedList.takeFirst();
168  m_selectedList.clear();
169 
170  updateVideoList();
171 }
172 
174 {
176  {
177  int index = m_selectedList.indexOf(item->GetData().value<VideoInfo *>());
178  if (index != -1)
179  m_selectedList.takeAt(index);
181  }
182  else
183  {
184  int index = m_selectedList.indexOf(item->GetData().value<VideoInfo *>());
185  if (index == -1)
186  m_selectedList.append(item->GetData().value<VideoInfo *>());
187 
189  }
190 }
191 
193 {
194  auto *v = item->GetData().value<VideoInfo *>();
195 
196  if (!v)
197  return;
198 
199  if (m_titleText)
200  m_titleText->SetText(v->title);
201 
202  if (m_plotText)
203  m_plotText->SetText(v->plot);
204 
205  if (m_coverImage)
206  {
207  if (v->coverfile != "" && v->coverfile != "No Cover")
208  {
209  m_coverImage->SetFilename(v->coverfile);
210  m_coverImage->Load();
211  }
212  else
213  {
214  m_coverImage->SetFilename("blank.png");
215  m_coverImage->Load();
216  }
217  }
218 
219  if (m_filesizeText)
220  {
221  if (v->size == 0)
222  {
223  struct stat fileinfo {};
224 
225  bool bExists = RemoteFile::Exists(v->filename, &fileinfo);
226  if (bExists)
227  v->size = (uint64_t)fileinfo.st_size;
228 
229  if (!bExists)
230  {
231  LOG(VB_GENERAL, LOG_ERR,
232  QString("VideoSelector: Cannot find file: %1")
233  .arg(v->filename));
234  }
235  }
236 
237  m_filesizeText->SetText(formatSize(v->size / 1024));
238  }
239 }
240 
242 {
243  // loop though selected videos and add them to the list
244  // remove any items that have been removed from the list
245  QList<ArchiveItem *> tempAList;
246  foreach (auto a, *m_archiveList)
247  {
248  bool found = false;
249 
250  foreach (auto v, m_selectedList)
251  {
252  if (a->type != "Video" || a->filename == v->filename)
253  {
254  found = true;
255  break;
256  }
257  }
258 
259  if (!found)
260  tempAList.append(a);
261  }
262 
263  foreach (auto x, tempAList)
264  m_archiveList->removeAll(x);
265 
266  // remove any items that are already in the list
267  QList<VideoInfo *> tempSList;
268  foreach (auto v, m_selectedList)
269  {
270  foreach (auto a, *m_archiveList)
271  {
272  if (a->filename == v->filename)
273  {
274  tempSList.append(v);
275  break;
276  }
277  }
278  }
279 
280  foreach (auto x, tempSList)
281  m_selectedList.removeAll(x);
282 
283  // add all that are left
284  foreach (auto v, m_selectedList)
285  {
286  auto *a = new ArchiveItem;
287  a->type = "Video";
288  a->title = v->title;
289  a->subtitle = "";
290  a->description = v->plot;
291  a->startDate = "";
292  a->startTime = "";
293  a->size = v->size;
294  a->newsize = v->size;
295  a->filename = v->filename;
296  a->hasCutlist = false;
297  a->useCutlist = false;
298  a->duration = 0;
299  a->cutDuration = 0;
300  a->videoWidth = 0;
301  a->videoHeight = 0;
302  a->fileCodec = "";
303  a->videoCodec = "";
304  a->encoderProfile = nullptr;
305  a->editedDetails = false;
306  m_archiveList->append(a);
307  }
308 
309  emit haveResult(true);
310  Close();
311 }
312 
314 {
315  emit haveResult(false);
316  Close();
317 }
318 
320 {
321  if (!m_videoList)
322  return;
323 
325 
326  if (m_categorySelector)
327  {
328  for (auto v : *m_videoList)
329  {
330  if (v->category == m_categorySelector->GetValue() ||
331  m_categorySelector->GetValue() == tr("All Videos"))
332  {
333  if (v->parentalLevel <= m_currentParentalLevel)
334  {
335  auto* item = new MythUIButtonListItem(m_videoButtonList,
336  v->title);
337  item->setCheckable(true);
338  if (m_selectedList.indexOf(v) != -1)
339  {
340  item->setChecked(MythUIButtonListItem::FullChecked);
341  }
342  else
343  {
344  item->setChecked(MythUIButtonListItem::NotChecked);
345  }
346 
347  item->SetData(QVariant::fromValue(v));
348  }
349  }
350  }
351  }
352 
353  if (m_videoButtonList->GetCount() > 0)
354  {
357  m_warningText->Hide();
358  }
359  else
360  {
361  m_warningText->Show();
362  m_titleText->Reset();
363  m_plotText->Reset();
364  m_coverImage->SetFilename("blank.png");
365  m_coverImage->Load();
367  }
368 }
369 
370 vector<VideoInfo *> *VideoSelector::getVideoListFromDB(void)
371 {
372  // get a list of category's
373  using CategoryMap = QMap<int, QString>;
374  CategoryMap categoryMap;
375  MSqlQuery query(MSqlQuery::InitCon());
376  query.prepare("SELECT intid, category FROM videocategory");
377 
378  if (query.exec())
379  {
380  while (query.next())
381  {
382  int id = query.value(0).toInt();
383  QString category = query.value(1).toString();
384  categoryMap.insert(id, category);
385  }
386  }
387 
388  query.prepare("SELECT intid, title, plot, length, filename, coverfile, "
389  "category, showlevel, subtitle, season, episode, host "
390  "FROM videometadata ORDER BY title,season,episode");
391 
392  if (query.exec() && query.size())
393  {
394  auto *videoList = new vector<VideoInfo*>;
395  QString artist;
396  QString genre;
397  QString episode;
398  while (query.next())
399  {
400  // Exclude iso images as they aren't supported
401  QString filename = query.value(4).toString();
402  if (filename.endsWith(".iso") || filename.endsWith(".ISO"))
403  continue;
404 
405  auto *info = new VideoInfo;
406 
407  info->id = query.value(0).toInt();
408  if (query.value(9).toInt() > 0)
409  {
410  episode = query.value(10).toString();
411  if (episode.size() < 2)
412  episode.prepend("0");
413  info->title = QString("%1 %2x%3 - %4")
414  .arg(query.value(1).toString())
415  .arg(query.value(9).toString())
416  .arg(episode)
417  .arg(query.value(8).toString());
418  }
419  else
420  info->title = query.value(1).toString();
421 
422  info->plot = query.value(2).toString();
423  info->size = 0; //query.value(3).toInt();
424  QString host = query.value(11).toString();
425 
426  // try to find the file locally
427  if (host.isEmpty())
428  {
429  // must already be a local filename?
430  info->filename = filename;
431  }
432  else
433  {
434  // if the file is on this host then we should be able to find it locally
435  if (host == gCoreContext->GetHostName())
436  {
437  StorageGroup videoGroup("Videos", gCoreContext->GetHostName(), false);
438  info->filename = videoGroup.FindFile(filename);
439 
440  // sanity check the file exists
441  if (!QFile::exists(info->filename))
442  {
443  LOG(VB_GENERAL, LOG_ERR,
444  QString("VideoSelector: Failed to find local file '%1'").arg(info->filename));
445  info->filename.clear();
446  }
447  }
448 
449  if (info->filename.isEmpty())
450  {
451  // file must not be local or doesn't exist
452  info->filename = generate_file_url("Videos", host, filename);
453  }
454  }
455 
456  LOG(VB_FILE, LOG_INFO,
457  QString("VideoSelector: found file '%1'").arg(info->filename));
458 
459  info->coverfile = query.value(5).toString();
460  info->category = categoryMap[query.value(6).toInt()];
461  info->parentalLevel = query.value(7).toInt();
462  if (info->category.isEmpty())
463  info->category = "(None)";
464  videoList->push_back(info);
465  }
466 
467  return videoList;
468  }
469 
470  LOG(VB_GENERAL, LOG_ERR, "VideoSelector: Failed to get any videos");
471 
472  return nullptr;
473 }
474 
476 {
478  QStringList categories;
479 
480  if (m_videoList && !m_videoList->empty())
481  {
482  for (auto v : *m_videoList)
483  {
484  if (categories.indexOf(v->category) == -1)
485  categories.append(v->category);
486  }
487  }
488  else
489  {
490  QTimer::singleShot(100, this, SLOT(cancelPressed()));
491  return;
492  }
493 
494  // sort and add categories to selector
495  categories.sort();
496 
497  if (m_categorySelector)
498  {
499  new MythUIButtonListItem(m_categorySelector, tr("All Videos"));
501 
502  for (int x = 0; x < categories.count(); x++)
503  {
504  new MythUIButtonListItem(m_categorySelector, categories[x]);
505  }
506  }
507 
508  setCategory(nullptr);
509 }
510 
512 {
513  (void)item;
514  updateVideoList();
515 }
516 
518 {
519  if (!m_videoList)
520  return;
521 
522  m_selectedList.clear();
523 
524  foreach (auto a, *m_archiveList)
525  {
526  foreach (auto v, *m_videoList)
527  {
528  if (v->filename == a->filename)
529  {
530  if (m_selectedList.indexOf(v) == -1)
531  m_selectedList.append(v);
532  break;
533  }
534  }
535  }
536 }
537 
539 {
541 }
542 
544 {
545  if (passwordValid)
546  {
547  m_currentParentalLevel = newLevel;
548  updateVideoList();
549  m_plText->SetText(QString::number(newLevel));
550  }
551  else
552  ShowOkPopup(tr("You need to enter a valid password for this parental level"));
553 }
554 
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
void titleChanged(MythUIButtonListItem *item)
void Show(void)
void toggleSelected(MythUIButtonListItem *item)
bool keyPressEvent(QKeyEvent *e) override
Key event handler.
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
void ShowMenu(void) override
MythUIImage * m_coverImage
Definition: videoselector.h:92
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
Basic menu dialog, message and a list of options.
~VideoSelector(void) override
static std::vector< VideoInfo * > * getVideoListFromDB(void)
void OKPressed(void)
int size(void) const
Definition: mythdbcon.h:203
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:135
MythScreenStack * GetStack(const QString &stackname)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythUIText * m_warningText
Definition: videoselector.h:85
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
void BuildFocusList(void)
void Check(ParentalLevel::Level fromLevel, ParentalLevel::Level toLevel)
ParentalLevel::Level m_currentParentalLevel
Definition: videoselector.h:81
MythUIButton * m_okButton
Definition: videoselector.h:86
void Hide(void)
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
void haveResult(bool ok)
MythUIText * m_plotText
Definition: videoselector.h:91
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
ParentalLevelChangeChecker * m_parentalLevelChecker
Definition: videoselector.h:75
MythUIButtonList * m_videoButtonList
Definition: videoselector.h:84
void getVideoList(void)
MythUIButton * m_cancelButton
Definition: videoselector.h:87
void setParentalLevel(ParentalLevel::Level level)
bool Create() override
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:83
void setCategory(MythUIButtonListItem *item)
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
CheckState state() const
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
std::vector< VideoInfo * > * m_videoList
Definition: videoselector.h:78
MythUIType * GetFocusWidget(void) const
QString type
Definition: archiveutil.h:54
void updateVideoList(void)
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
void cancelPressed(void)
MythMainWindow * GetMythMainWindow(void)
void selectAll(void)
virtual QString GetValue() const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
static QString formatSize(int64_t sizeKB, int prec)
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:460
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
MythUIText * m_filesizeText
Definition: videoselector.h:90
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString FindFile(const QString &filename)
MythUIButtonList * m_categorySelector
Definition: videoselector.h:88
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
void parentalLevelChanged(bool passwordValid, ParentalLevel::Level newLevel)
void updateSelectedList(void)
QString generate_file_url(const QString &storage_group, const QString &host, const QString &path)
Definition: videoutils.h:65
void SetItemCurrent(MythUIButtonListItem *item)
MythUIText * m_titleText
Definition: videoselector.h:89
bool SetFocusWidget(MythUIType *widget=nullptr)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
MythUIButtonListItem * GetItemFirst() const
Screen in which all other widgets are contained and rendered.
MythUIText * m_plText
Definition: videoselector.h:83
QList< ArchiveItem * > * m_archiveList
Definition: videoselector.h:77
QString GetHostName(void)
void clearAll(void)
QList< VideoInfo * > m_selectedList
Definition: videoselector.h:79
VideoSelector(MythScreenStack *parent, QList< ArchiveItem * > *archiveList)
void setChecked(CheckState state)
MythUIButtonListItem * GetItemCurrent() const