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  MythDialogBox *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  vector<VideoInfo *>::iterator 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  VideoInfo *v = item->GetData().value<VideoInfo *>();
199 
200  if (!v)
201  return;
202 
203  if (m_titleText)
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  {
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  bool bExists;
228 
229  struct stat fileinfo;
230  memset(&fileinfo, 0, sizeof(fileinfo ));
231 
232  bExists = RemoteFile::Exists(v->filename, &fileinfo);
233  if (bExists)
234  v->size = (uint64_t)fileinfo.st_size;
235 
236  if (!bExists)
237  LOG(VB_GENERAL, LOG_ERR,
238  QString("VideoSelector: Cannot find file: %1")
239  .arg(v->filename));
240  }
241 
242  m_filesizeText->SetText(formatSize(v->size / 1024));
243  }
244 }
245 
247 {
248  // loop though selected videos and add them to the list
249  VideoInfo *v;
250  ArchiveItem *a;
251 
252  // remove any items that have been removed from the list
253  QList<ArchiveItem *> tempAList;
254  for (int x = 0; x < m_archiveList->size(); x++)
255  {
256  a = m_archiveList->at(x);
257  bool found = false;
258 
259  for (int y = 0; y < m_selectedList.size(); y++)
260  {
261  v = m_selectedList.at(y);
262  if (a->type != "Video" || a->filename == v->filename)
263  {
264  found = true;
265  break;
266  }
267  }
268 
269  if (!found)
270  tempAList.append(a);
271  }
272 
273  for (int x = 0; x < tempAList.size(); x++)
274  m_archiveList->removeAll(tempAList.at(x));
275 
276  // remove any items that are already in the list
277  QList<VideoInfo *> tempSList;
278  for (int x = 0; x < m_selectedList.size(); x++)
279  {
280  v = m_selectedList.at(x);
281 
282  for (int y = 0; y < m_archiveList->size(); y++)
283  {
284  a = m_archiveList->at(y);
285  if (a->filename == v->filename)
286  {
287  tempSList.append(v);
288  break;
289  }
290  }
291  }
292 
293  for (int x = 0; x < tempSList.size(); x++)
294  m_selectedList.removeAll(tempSList.at(x));
295 
296  // add all that are left
297  for (int x = 0; x < m_selectedList.size(); x++)
298  {
299  v = m_selectedList.at(x);
300  a = new ArchiveItem;
301  a->type = "Video";
302  a->title = v->title;
303  a->subtitle = "";
304  a->description = v->plot;
305  a->startDate = "";
306  a->startTime = "";
307  a->size = v->size;
308  a->newsize = v->size;
309  a->filename = v->filename;
310  a->hasCutlist = false;
311  a->useCutlist = false;
312  a->duration = 0;
313  a->cutDuration = 0;
314  a->videoWidth = 0;
315  a->videoHeight = 0;
316  a->fileCodec = "";
317  a->videoCodec = "";
318  a->encoderProfile = nullptr;
319  a->editedDetails = false;
320  m_archiveList->append(a);
321  }
322 
323  emit haveResult(true);
324  Close();
325 }
326 
328 {
329  emit haveResult(false);
330  Close();
331 }
332 
334 {
335  if (!m_videoList)
336  return;
337 
339 
340  if (m_categorySelector)
341  {
342  vector<VideoInfo *>::iterator i = m_videoList->begin();
343  for ( ; i != m_videoList->end(); ++i)
344  {
345  VideoInfo *v = *i;
346 
347  if (v->category == m_categorySelector->GetValue() ||
348  m_categorySelector->GetValue() == tr("All Videos"))
349  {
351  {
354  item->setCheckable(true);
355  if (m_selectedList.indexOf(v) != -1)
356  {
358  }
359  else
360  {
362  }
363 
364  item->SetData(qVariantFromValue(v));
365  }
366  }
367  }
368  }
369 
370  if (m_videoButtonList->GetCount() > 0)
371  {
374  m_warningText->Hide();
375  }
376  else
377  {
378  m_warningText->Show();
379  m_titleText->Reset();
380  m_plotText->Reset();
381  m_coverImage->SetFilename("blank.png");
382  m_coverImage->Load();
384  }
385 }
386 
387 vector<VideoInfo *> *VideoSelector::getVideoListFromDB(void)
388 {
389  // get a list of category's
390  typedef QMap<int, QString> CategoryMap;
391  CategoryMap categoryMap;
392  MSqlQuery query(MSqlQuery::InitCon());
393  query.prepare("SELECT intid, category FROM videocategory");
394 
395  if (query.exec())
396  {
397  while (query.next())
398  {
399  int id = query.value(0).toInt();
400  QString category = query.value(1).toString();
401  categoryMap.insert(id, category);
402  }
403  }
404 
405  query.prepare("SELECT intid, title, plot, length, filename, coverfile, "
406  "category, showlevel, subtitle, season, episode, host "
407  "FROM videometadata ORDER BY title,season,episode");
408 
409  if (query.exec() && query.size())
410  {
411  vector<VideoInfo*> *videoList = new vector<VideoInfo*>;
412  QString artist, genre, episode;
413  while (query.next())
414  {
415  // Exclude iso images as they aren't supported
416  QString filename = query.value(4).toString();
417  if (filename.endsWith(".iso") || filename.endsWith(".ISO"))
418  continue;
419 
420  VideoInfo *info = new VideoInfo;
421 
422  info->id = query.value(0).toInt();
423  if (query.value(9).toInt() > 0)
424  {
425  episode = query.value(10).toString();
426  if (episode.size() < 2)
427  episode.prepend("0");
428  info->title = QString("%1 %2x%3 - %4")
429  .arg(query.value(1).toString())
430  .arg(query.value(9).toString())
431  .arg(episode)
432  .arg(query.value(8).toString());
433  }
434  else
435  info->title = query.value(1).toString();
436 
437  info->plot = query.value(2).toString();
438  info->size = 0; //query.value(3).toInt();
439  QString host = query.value(11).toString();
440 
441  // try to find the file locally
442  if (host.isEmpty())
443  {
444  // must already be a local filename?
445  info->filename = filename;
446  }
447  else
448  {
449  // if the file is on this host then we should be able to find it locally
450  if (host == gCoreContext->GetHostName())
451  {
452  StorageGroup videoGroup("Videos", gCoreContext->GetHostName(), false);
453  info->filename = videoGroup.FindFile(filename);
454 
455  // sanity check the file exists
456  if (!QFile::exists(info->filename))
457  {
458  LOG(VB_GENERAL, LOG_ERR,
459  QString("VideoSelector: Failed to find local file '%1'").arg(info->filename));
460  info->filename.clear();
461  }
462  }
463 
464  if (info->filename.isEmpty())
465  {
466  // file must not be local or doesn't exist
467  info->filename = generate_file_url("Videos", host, filename);
468  }
469  }
470 
471  LOG(VB_FILE, LOG_INFO,
472  QString("VideoSelector: found file '%1'").arg(info->filename));
473 
474  info->coverfile = query.value(5).toString();
475  info->category = categoryMap[query.value(6).toInt()];
476  info->parentalLevel = query.value(7).toInt();
477  if (info->category.isEmpty())
478  info->category = "(None)";
479  videoList->push_back(info);
480  }
481 
482  return videoList;
483  }
484 
485  LOG(VB_GENERAL, LOG_ERR, "VideoSelector: Failed to get any videos");
486 
487  return nullptr;
488 }
489 
491 {
493  QStringList categories;
494 
495  if (m_videoList && !m_videoList->empty())
496  {
497  vector<VideoInfo *>::iterator i = m_videoList->begin();
498  for ( ; i != m_videoList->end(); ++i)
499  {
500  VideoInfo *v = *i;
501 
502  if (categories.indexOf(v->category) == -1)
503  categories.append(v->category);
504  }
505  }
506  else
507  {
508  QTimer::singleShot(100, this, SLOT(cancelPressed()));
509  return;
510  }
511 
512  // sort and add categories to selector
513  categories.sort();
514 
515  if (m_categorySelector)
516  {
517  new MythUIButtonListItem(m_categorySelector, tr("All Videos"));
519 
520  for (int x = 0; x < categories.count(); x++)
521  {
522  new MythUIButtonListItem(m_categorySelector, categories[x]);
523  }
524  }
525 
526  setCategory(nullptr);
527 }
528 
530 {
531  (void)item;
532  updateVideoList();
533 }
534 
536 {
537  if (!m_videoList)
538  return;
539 
540  m_selectedList.clear();
541 
542  for (int x = 0; x < m_archiveList->size(); x++)
543  {
544  ArchiveItem *a = m_archiveList->at(x);
545  for (size_t y = 0; y < m_videoList->size(); y++)
546  {
547  VideoInfo *v = m_videoList->at(y);
548  if (v->filename == a->filename)
549  {
550  if (m_selectedList.indexOf(v) == -1)
551  m_selectedList.append(v);
552  break;
553  }
554  }
555  }
556 }
557 
559 {
561 }
562 
564 {
565  if (passwordValid)
566  {
567  m_currentParentalLevel = newLevel;
568  updateVideoList();
569  m_plText->SetText(QString::number(newLevel));
570  }
571  else
572  ShowOkPopup(tr("You need to enter a valid password for this parental level"));
573 }
574 
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
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
void SetData(QVariant data)
bool hasCutlist
Definition: archiveutil.h:69
MythUIImage * m_coverImage
Definition: videoselector.h:92
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
bool useCutlist
Definition: archiveutil.h:70
Basic menu dialog, message and a list of options.
QString coverfile
Definition: videoselector.h:33
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:136
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
int videoWidth
Definition: archiveutil.h:68
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)
int64_t newsize
Definition: archiveutil.h:62
QString startTime
Definition: archiveutil.h:59
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 setCheckable(bool flag)
void AddButton(const QString &title, QVariant data=0, bool newMenu=false, bool setCurrent=false)
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
QString fileCodec
Definition: archiveutil.h:66
bool editedDetails
Definition: archiveutil.h:71
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:84
void setCategory(MythUIButtonListItem *item)
int cutDuration
Definition: archiveutil.h:64
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: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
QString subtitle
Definition: archiveutil.h:56
static QString formatSize(int64_t sizeKB, int prec)
QString videoCodec
Definition: archiveutil.h:67
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:807
MythUIText * m_filesizeText
Definition: videoselector.h:90
bool keyPressEvent(QKeyEvent *) override
Key event handler.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString startDate
Definition: archiveutil.h:58
QString title
Definition: archiveutil.h:55
MythUIButtonList * m_categorySelector
Definition: videoselector.h:88
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
EncoderProfile * encoderProfile
Definition: archiveutil.h:65
void SetReturnEvent(QObject *retobject, const QString &resultid)
void parentalLevelChanged(bool passwordValid, ParentalLevel::Level newLevel)
void updateSelectedList(void)
int videoHeight
Definition: archiveutil.h:68
QString generate_file_url(const QString &storage_group, const QString &host, const QString &path)
Definition: videoutils.h:65
QString description
Definition: archiveutil.h:57
void SetItemCurrent(MythUIButtonListItem *item)
MythUIText * m_titleText
Definition: videoselector.h:89
uint64_t size
Definition: videoselector.h:35
bool SetFocusWidget(MythUIType *widget=nullptr)
int64_t size
Definition: archiveutil.h:61
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)
QString title
Definition: videoselector.h:29
MythUIButtonListItem * GetItemCurrent() const
bool Create(void) override