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