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  auto i = m_videoList->begin();
159  for ( ; i != m_videoList->end(); ++i)
160  {
161  VideoInfo *v = *i;
162  m_selectedList.append(v);
163  }
164 
165  updateVideoList();
166 }
167 
169 {
170  while (!m_selectedList.isEmpty())
171  m_selectedList.takeFirst();
172  m_selectedList.clear();
173 
174  updateVideoList();
175 }
176 
178 {
180  {
181  int index = m_selectedList.indexOf(item->GetData().value<VideoInfo *>());
182  if (index != -1)
183  m_selectedList.takeAt(index);
185  }
186  else
187  {
188  int index = m_selectedList.indexOf(item->GetData().value<VideoInfo *>());
189  if (index == -1)
190  m_selectedList.append(item->GetData().value<VideoInfo *>());
191 
193  }
194 }
195 
197 {
198  auto *v = item->GetData().value<VideoInfo *>();
199 
200  if (!v)
201  return;
202 
203  if (m_titleText)
204  m_titleText->SetText(v->title);
205 
206  if (m_plotText)
207  m_plotText->SetText(v->plot);
208 
209  if (m_coverImage)
210  {
211  if (v->coverfile != "" && v->coverfile != "No Cover")
212  {
213  m_coverImage->SetFilename(v->coverfile);
214  m_coverImage->Load();
215  }
216  else
217  {
218  m_coverImage->SetFilename("blank.png");
219  m_coverImage->Load();
220  }
221  }
222 
223  if (m_filesizeText)
224  {
225  if (v->size == 0)
226  {
227  struct stat fileinfo {};
228 
229  bool bExists = RemoteFile::Exists(v->filename, &fileinfo);
230  if (bExists)
231  v->size = (uint64_t)fileinfo.st_size;
232 
233  if (!bExists)
234  LOG(VB_GENERAL, LOG_ERR,
235  QString("VideoSelector: Cannot find file: %1")
236  .arg(v->filename));
237  }
238 
239  m_filesizeText->SetText(formatSize(v->size / 1024));
240  }
241 }
242 
244 {
245  // loop though selected videos and add them to the list
246  // remove any items that have been removed from the list
247  QList<ArchiveItem *> tempAList;
248  for (int x = 0; x < m_archiveList->size(); x++)
249  {
250  ArchiveItem *a = m_archiveList->at(x);
251  bool found = false;
252 
253  for (int y = 0; y < m_selectedList.size(); y++)
254  {
255  VideoInfo *v = m_selectedList.at(y);
256  if (a->type != "Video" || a->filename == v->filename)
257  {
258  found = true;
259  break;
260  }
261  }
262 
263  if (!found)
264  tempAList.append(a);
265  }
266 
267  for (int x = 0; x < tempAList.size(); x++)
268  m_archiveList->removeAll(tempAList.at(x));
269 
270  // remove any items that are already in the list
271  QList<VideoInfo *> tempSList;
272  for (int x = 0; x < m_selectedList.size(); x++)
273  {
274  VideoInfo *v = m_selectedList.at(x);
275 
276  for (int y = 0; y < m_archiveList->size(); y++)
277  {
278  ArchiveItem *a = m_archiveList->at(y);
279  if (a->filename == v->filename)
280  {
281  tempSList.append(v);
282  break;
283  }
284  }
285  }
286 
287  for (int x = 0; x < tempSList.size(); x++)
288  m_selectedList.removeAll(tempSList.at(x));
289 
290  // add all that are left
291  for (int x = 0; x < m_selectedList.size(); x++)
292  {
293  VideoInfo *v = m_selectedList.at(x);
294  auto *a = new ArchiveItem;
295  a->type = "Video";
296  a->title = v->title;
297  a->subtitle = "";
298  a->description = v->plot;
299  a->startDate = "";
300  a->startTime = "";
301  a->size = v->size;
302  a->newsize = v->size;
303  a->filename = v->filename;
304  a->hasCutlist = false;
305  a->useCutlist = false;
306  a->duration = 0;
307  a->cutDuration = 0;
308  a->videoWidth = 0;
309  a->videoHeight = 0;
310  a->fileCodec = "";
311  a->videoCodec = "";
312  a->encoderProfile = nullptr;
313  a->editedDetails = false;
314  m_archiveList->append(a);
315  }
316 
317  emit haveResult(true);
318  Close();
319 }
320 
322 {
323  emit haveResult(false);
324  Close();
325 }
326 
328 {
329  if (!m_videoList)
330  return;
331 
333 
334  if (m_categorySelector)
335  {
336  auto i = m_videoList->begin();
337  for ( ; i != m_videoList->end(); ++i)
338  {
339  VideoInfo *v = *i;
340 
341  if (v->category == m_categorySelector->GetValue() ||
342  m_categorySelector->GetValue() == tr("All Videos"))
343  {
345  {
346  auto* item = new MythUIButtonListItem(m_videoButtonList,
347  v->title);
348  item->setCheckable(true);
349  if (m_selectedList.indexOf(v) != -1)
350  {
351  item->setChecked(MythUIButtonListItem::FullChecked);
352  }
353  else
354  {
355  item->setChecked(MythUIButtonListItem::NotChecked);
356  }
357 
358  item->SetData(qVariantFromValue(v));
359  }
360  }
361  }
362  }
363 
364  if (m_videoButtonList->GetCount() > 0)
365  {
368  m_warningText->Hide();
369  }
370  else
371  {
372  m_warningText->Show();
373  m_titleText->Reset();
374  m_plotText->Reset();
375  m_coverImage->SetFilename("blank.png");
376  m_coverImage->Load();
378  }
379 }
380 
381 vector<VideoInfo *> *VideoSelector::getVideoListFromDB(void)
382 {
383  // get a list of category's
384  using CategoryMap = QMap<int, QString>;
385  CategoryMap categoryMap;
386  MSqlQuery query(MSqlQuery::InitCon());
387  query.prepare("SELECT intid, category FROM videocategory");
388 
389  if (query.exec())
390  {
391  while (query.next())
392  {
393  int id = query.value(0).toInt();
394  QString category = query.value(1).toString();
395  categoryMap.insert(id, category);
396  }
397  }
398 
399  query.prepare("SELECT intid, title, plot, length, filename, coverfile, "
400  "category, showlevel, subtitle, season, episode, host "
401  "FROM videometadata ORDER BY title,season,episode");
402 
403  if (query.exec() && query.size())
404  {
405  auto *videoList = new vector<VideoInfo*>;
406  QString artist, genre, episode;
407  while (query.next())
408  {
409  // Exclude iso images as they aren't supported
410  QString filename = query.value(4).toString();
411  if (filename.endsWith(".iso") || filename.endsWith(".ISO"))
412  continue;
413 
414  auto *info = new VideoInfo;
415 
416  info->id = query.value(0).toInt();
417  if (query.value(9).toInt() > 0)
418  {
419  episode = query.value(10).toString();
420  if (episode.size() < 2)
421  episode.prepend("0");
422  info->title = QString("%1 %2x%3 - %4")
423  .arg(query.value(1).toString())
424  .arg(query.value(9).toString())
425  .arg(episode)
426  .arg(query.value(8).toString());
427  }
428  else
429  info->title = query.value(1).toString();
430 
431  info->plot = query.value(2).toString();
432  info->size = 0; //query.value(3).toInt();
433  QString host = query.value(11).toString();
434 
435  // try to find the file locally
436  if (host.isEmpty())
437  {
438  // must already be a local filename?
439  info->filename = filename;
440  }
441  else
442  {
443  // if the file is on this host then we should be able to find it locally
444  if (host == gCoreContext->GetHostName())
445  {
446  StorageGroup videoGroup("Videos", gCoreContext->GetHostName(), false);
447  info->filename = videoGroup.FindFile(filename);
448 
449  // sanity check the file exists
450  if (!QFile::exists(info->filename))
451  {
452  LOG(VB_GENERAL, LOG_ERR,
453  QString("VideoSelector: Failed to find local file '%1'").arg(info->filename));
454  info->filename.clear();
455  }
456  }
457 
458  if (info->filename.isEmpty())
459  {
460  // file must not be local or doesn't exist
461  info->filename = generate_file_url("Videos", host, filename);
462  }
463  }
464 
465  LOG(VB_FILE, LOG_INFO,
466  QString("VideoSelector: found file '%1'").arg(info->filename));
467 
468  info->coverfile = query.value(5).toString();
469  info->category = categoryMap[query.value(6).toInt()];
470  info->parentalLevel = query.value(7).toInt();
471  if (info->category.isEmpty())
472  info->category = "(None)";
473  videoList->push_back(info);
474  }
475 
476  return videoList;
477  }
478 
479  LOG(VB_GENERAL, LOG_ERR, "VideoSelector: Failed to get any videos");
480 
481  return nullptr;
482 }
483 
485 {
487  QStringList categories;
488 
489  if (m_videoList && !m_videoList->empty())
490  {
491  auto i = m_videoList->begin();
492  for ( ; i != m_videoList->end(); ++i)
493  {
494  VideoInfo *v = *i;
495 
496  if (categories.indexOf(v->category) == -1)
497  categories.append(v->category);
498  }
499  }
500  else
501  {
502  QTimer::singleShot(100, this, SLOT(cancelPressed()));
503  return;
504  }
505 
506  // sort and add categories to selector
507  categories.sort();
508 
509  if (m_categorySelector)
510  {
511  new MythUIButtonListItem(m_categorySelector, tr("All Videos"));
513 
514  for (int x = 0; x < categories.count(); x++)
515  {
516  new MythUIButtonListItem(m_categorySelector, categories[x]);
517  }
518  }
519 
520  setCategory(nullptr);
521 }
522 
524 {
525  (void)item;
526  updateVideoList();
527 }
528 
530 {
531  if (!m_videoList)
532  return;
533 
534  m_selectedList.clear();
535 
536  for (int x = 0; x < m_archiveList->size(); x++)
537  {
538  ArchiveItem *a = m_archiveList->at(x);
539  for (size_t y = 0; y < m_videoList->size(); y++)
540  {
541  VideoInfo *v = m_videoList->at(y);
542  if (v->filename == a->filename)
543  {
544  if (m_selectedList.indexOf(v) == -1)
545  m_selectedList.append(v);
546  break;
547  }
548  }
549  }
550 }
551 
553 {
555 }
556 
558 {
559  if (passwordValid)
560  {
561  m_currentParentalLevel = newLevel;
562  updateVideoList();
563  m_plText->SetText(QString::number(newLevel));
564  }
565  else
566  ShowOkPopup(tr("You need to enter a valid password for this parental level"));
567 }
568 
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:781
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.
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)
int parentalLevel
Definition: videoselector.h:34
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythUIText * m_warningText
Definition: videoselector.h:85
QString plot
Definition: videoselector.h:30
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
QString filename
Definition: archiveutil.h:60
void Hide(void)
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
void haveResult(bool ok)
QString filename
Definition: videoselector.h:32
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=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:534
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
static QString formatSize(int64_t sizeKB, int prec)
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:461
QString category
Definition: videoselector.h:31
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:806
MythUIText * m_filesizeText
Definition: videoselector.h:90
bool keyPressEvent(QKeyEvent *) override
Key event handler.
#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
uint64_t size
Definition: videoselector.h:35
bool SetFocusWidget(MythUIType *widget=nullptr)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:602
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)
QString title
Definition: videoselector.h:29
MythUIButtonListItem * GetItemCurrent() const