MythTV  master
exportnative.cpp
Go to the documentation of this file.
1 #include <cstdlib>
2 #include <cstdlib>
3 #include <iostream>
4 #include <unistd.h>
5 
6 // qt
7 #include <QFile>
8 #include <QKeyEvent>
9 #include <QTextStream>
10 #include <QDomDocument>
11 
12 // myth
13 #include <mythcontext.h>
14 #include <remoteutil.h>
15 #include <programinfo.h>
16 #include <mythdb.h>
17 #include <mythdialogbox.h>
18 #include <mythuitext.h>
19 #include <mythuibutton.h>
20 #include <mythuicheckbox.h>
21 #include <mythuibuttonlist.h>
22 #include <mythuiprogressbar.h>
23 #include <mythmainwindow.h>
24 #include <mythsystemlegacy.h>
25 #include <exitcodes.h>
26 
27 // mytharchive
28 #include "exportnative.h"
29 #include "fileselector.h"
30 #include "archiveutil.h"
31 #include "recordingselector.h"
32 #include "videoselector.h"
33 #include "logviewer.h"
34 
36 {
38 
39  while (!m_archiveList.isEmpty())
40  delete m_archiveList.takeFirst();
41  m_archiveList.clear();
42 }
43 
45 {
46  // Load the theme for this screen
47  bool foundtheme = LoadWindowFromXML("mythnative-ui.xml", "exportnative", this);
48  if (!foundtheme)
49  return false;
50 
51  bool err = false;
52  UIUtilE::Assign(this, m_nextButton, "next_button", &err);
53  UIUtilE::Assign(this, m_prevButton, "prev_button", &err);
54  UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
55 
56  UIUtilE::Assign(this, m_titleText, "progtitle", &err);
57  UIUtilE::Assign(this, m_datetimeText, "progdatetime", &err);
58  UIUtilE::Assign(this, m_descriptionText, "progdescription", &err);
59  UIUtilE::Assign(this, m_filesizeText, "filesize", &err);
60  UIUtilE::Assign(this, m_nofilesText, "nofiles", &err);
61  UIUtilE::Assign(this, m_sizeBar, "size_bar", &err);
62  UIUtilE::Assign(this, m_archiveButtonList, "archivelist", &err);
63  UIUtilE::Assign(this, m_addrecordingButton, "addrecording_button", &err);
64  UIUtilE::Assign(this, m_addvideoButton, "addvideo_button", &err);
65 
66  UIUtilW::Assign(this, m_maxsizeText, "maxsize");
67  UIUtilW::Assign(this, m_minsizeText, "minsize");
68  UIUtilW::Assign(this, m_currsizeText, "currentsize");
69  UIUtilW::Assign(this, m_currsizeErrText, "currentsize_error");
70 
71  if (err)
72  {
73  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'exportnative'");
74  return false;
75  }
76 
77  connect(m_nextButton, SIGNAL(Clicked()), this, SLOT(handleNextPage()));
78  connect(m_prevButton, SIGNAL(Clicked()), this, SLOT(handlePrevPage()));
79  connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(handleCancel()));
80 
81 
83  connect(m_archiveButtonList, SIGNAL(itemSelected(MythUIButtonListItem *)),
84  this, SLOT(titleChanged(MythUIButtonListItem *)));
85 
86  connect(m_addrecordingButton, SIGNAL(Clicked()), this, SLOT(handleAddRecording()));
87  connect(m_addvideoButton, SIGNAL(Clicked()), this, SLOT(handleAddVideo()));
88 
90 
92 
94 
95  return true;
96 }
97 
98 bool ExportNative::keyPressEvent(QKeyEvent *event)
99 {
100  if (GetFocusWidget()->keyPressEvent(event))
101  return true;
102 
103  QStringList actions;
104  bool handled = GetMythMainWindow()->TranslateKeyPress("Archive", event, actions);
105 
106  for (int i = 0; i < actions.size() && !handled; i++)
107  {
108  QString action = actions[i];
109  handled = true;
110 
111  if (action == "MENU")
112  {
113  ShowMenu();
114  }
115  else if (action == "DELETE")
116  {
117  removeItem();
118  }
119 
120  else
121  handled = false;
122  }
123 
124  if (!handled && MythScreenType::keyPressEvent(event))
125  handled = true;
126 
127  return handled;
128 }
129 
131 {
132  int64_t size = 0;
133 
134  foreach (auto a, m_archiveList)
135  size += a->size;
136 
137  m_usedSpace = size / 1024 / 1024;
138  uint freeSpace = m_archiveDestination.freeSpace / 1024;
139 
140  QString tmpSize;
141 
142  m_sizeBar->SetTotal(freeSpace);
144 
145  tmpSize.sprintf("%0d Mb", freeSpace);
146 
147  if (m_maxsizeText)
148  m_maxsizeText->SetText(tmpSize);
149 
150  if (m_minsizeText)
151  m_minsizeText->SetText("0 Mb");
152 
153  tmpSize.sprintf("%0d Mb", m_usedSpace);
154 
155  if (m_usedSpace > freeSpace)
156  {
157  if (m_currsizeText)
158  m_currsizeText->Hide();
159 
160  if (m_currsizeErrText)
161  {
163  m_currsizeErrText->SetText(tmpSize);
164  }
165  }
166  else
167  {
168  if (m_currsizeErrText)
170 
171  if (m_currsizeText)
172  {
173  m_currsizeText->Show();
174  m_currsizeText->SetText(tmpSize);
175  }
176  }
177 }
178 
180 {
181  auto *a = item->GetData().value<ArchiveItem *>();
182  if (!a)
183  return;
184 
185  m_titleText->SetText(a->title);
186 
187  m_datetimeText->SetText(a->startDate + " " + a->startTime);
188 
190  (a->subtitle != "" ? a->subtitle + "\n" : "") + a->description);
191 
192  m_filesizeText->SetText(formatSize(a->size / 1024, 2));
193 }
194 
196 {
197  if (m_archiveList.empty())
198  {
199  ShowOkPopup(tr("You need to add at least one item to archive!"));
200  return;
201  }
202 
203  runScript();
204 
206  Close();
207 }
208 
210 {
211  Close();
212 }
213 
215 {
217  Close();
218 }
219 
221 {
223 
224  if (m_archiveList.empty())
225  {
226  m_titleText->Reset();
230  m_nofilesText->Show();
231  }
232  else
233  {
234  foreach (auto a, m_archiveList)
235  {
236  auto* item = new MythUIButtonListItem(m_archiveButtonList, a->title);
237  item->SetData(QVariant::fromValue(a));
238  }
239 
242  m_nofilesText->Hide();
243  }
244 
245  updateSizeBar();
246 }
247 
249 {
250  while (!m_archiveList.isEmpty())
251  delete m_archiveList.takeFirst();
252  m_archiveList.clear();
253 
254  MSqlQuery query(MSqlQuery::InitCon());
255  query.prepare("SELECT intid, type, title, subtitle, description, size, "
256  "startdate, starttime, filename, hascutlist "
257  "FROM archiveitems WHERE type = 'Recording' OR type = 'Video' "
258  "ORDER BY title, subtitle");
259 
260  if (query.exec())
261  {
262  while (query.next())
263  {
264  auto *item = new ArchiveItem;
265 
266  item->id = query.value(0).toInt();
267  item->type = query.value(1).toString();
268  item->title = query.value(2).toString();
269  item->subtitle = query.value(3).toString();
270  item->description = query.value(4).toString();
271  item->size = query.value(5).toLongLong();
272  item->startDate = query.value(6).toString();
273  item->startTime = query.value(7).toString();
274  item->filename = query.value(8).toString();
275  item->hasCutlist = (query.value(9).toInt() > 0);
276  item->useCutlist = false;
277  item->editedDetails = false;
278 
279  m_archiveList.append(item);
280  }
281  }
282 }
283 
285 {
288 }
289 
291 {
292  m_bCreateISO = (gCoreContext->GetSetting("MythNativeCreateISO", "0") == "1");
293  m_bDoBurn = (gCoreContext->GetSetting("MythNativeBurnDVDr", "1") == "1");
294  m_bEraseDvdRw = (gCoreContext->GetSetting("MythNativeEraseDvdRw", "0") == "1");
295  m_saveFilename = gCoreContext->GetSetting("MythNativeSaveFilename", "");
296 }
297 
299 {
300  // remove all old archive items from DB
301  MSqlQuery query(MSqlQuery::InitCon());
302  query.prepare("DELETE FROM archiveitems;");
303  if (!query.exec())
304  MythDB::DBError("ExportNative::saveConfiguration - "
305  "deleting archiveitems", query);
306 
307  // save new list of archive items to DB
308  query.prepare("INSERT INTO archiveitems (type, title, subtitle, "
309  "description, startdate, starttime, size, filename, hascutlist, "
310  "duration, cutduration, videowidth, videoheight, filecodec,"
311  "videocodec, encoderprofile) "
312  "VALUES(:TYPE, :TITLE, :SUBTITLE, :DESCRIPTION, :STARTDATE, "
313  ":STARTTIME, :SIZE, :FILENAME, :HASCUTLIST, :DURATION, "
314  ":CUTDURATION, :VIDEOWIDTH, :VIDEOHEIGHT, :FILECODEC, "
315  ":VIDEOCODEC, :ENCODERPROFILE);");
316  foreach (auto a, m_archiveList)
317  {
318  query.bindValue(":TYPE", a->type);
319  query.bindValue(":TITLE", a->title);
320  query.bindValue(":SUBTITLE", a->subtitle);
321  query.bindValue(":DESCRIPTION", a->description);
322  query.bindValue(":STARTDATE", a->startDate);
323  query.bindValue(":STARTTIME", a->startTime);
324  query.bindValue(":SIZE", 0);
325  query.bindValue(":FILENAME", a->filename);
326  query.bindValue(":HASCUTLIST", a->hasCutlist);
327  query.bindValue(":DURATION", 0);
328  query.bindValue(":CUTDURATION", 0);
329  query.bindValue(":VIDEOWIDTH", 0);
330  query.bindValue(":VIDEOHEIGHT", 0);
331  query.bindValue(":FILECODEC", "");
332  query.bindValue(":VIDEOCODEC", "");
333  query.bindValue(":ENCODERPROFILE", "");
334 
335  if (!query.exec())
336  MythDB::DBError("archive item insert", query);
337  }
338 }
339 
341 {
342  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
343 
344  auto *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
345 
346  if (menuPopup->Create())
347  popupStack->AddScreen(menuPopup);
348 
349  menuPopup->SetReturnEvent(this, "action");
350 
351  menuPopup->AddButton(tr("Remove Item"), SLOT(removeItem()));
352 }
353 
355 {
357  auto *curItem = item->GetData().value<ArchiveItem *>();
358 
359  if (!curItem)
360  return;
361 
362  MSqlQuery query(MSqlQuery::InitCon());
363  query.prepare("DELETE FROM archiveitems WHERE filename = :FILENAME;");
364  query.bindValue(":FILENAME", curItem->filename);
365  if (query.exec() && query.numRowsAffected())
366  {
367  getArchiveList();
368  }
369 }
370 
372 {
373  QDomDocument doc("NATIVEARCHIVEJOB");
374 
375  QDomElement root = doc.createElement("nativearchivejob");
376  doc.appendChild(root);
377 
378  QDomElement job = doc.createElement("job");
379  root.appendChild(job);
380 
381  QDomElement media = doc.createElement("media");
382  job.appendChild(media);
383 
384  // now loop though selected archive items and add them to the xml file
385  foreach (auto a, m_archiveList)
386  {
387  QDomElement file = doc.createElement("file");
388  file.setAttribute("type", a->type.toLower() );
389  file.setAttribute("title", a->title);
390  file.setAttribute("filename", a->filename);
391  file.setAttribute("delete", "0");
392  media.appendChild(file);
393  }
394 
395  // add the options to the xml file
396  QDomElement options = doc.createElement("options");
397  options.setAttribute("createiso", static_cast<int>(m_bCreateISO));
398  options.setAttribute("doburn", static_cast<int>(m_bDoBurn));
399  options.setAttribute("mediatype", m_archiveDestination.type);
400  options.setAttribute("dvdrsize", (qint64)m_archiveDestination.freeSpace);
401  options.setAttribute("erasedvdrw", static_cast<int>(m_bEraseDvdRw));
402  options.setAttribute("savedirectory", m_saveFilename);
403  job.appendChild(options);
404 
405  // finally save the xml to the file
406  QFile f(filename);
407  if (!f.open(QIODevice::WriteOnly))
408  {
409  LOG(VB_GENERAL, LOG_ERR,
410  QString("ExportNative::createConfigFile: "
411  "Failed to open file for writing - %1") .arg(filename));
412  return;
413  }
414 
415  QTextStream t(&f);
416  t << doc.toString(4);
417  f.close();
418 }
419 
421 {
422  QString tempDir = getTempDirectory();
423  QString logDir = tempDir + "logs";
424  QString configDir = tempDir + "config";
425  QString commandline;
426 
427  // remove any existing logs
428  myth_system("rm -f " + logDir + "/*.log");
429 
430  // remove cancel flag file if present
431  if (QFile::exists(logDir + "/mythburncancel.lck"))
432  QFile::remove(logDir + "/mythburncancel.lck");
433 
434  createConfigFile(configDir + "/mydata.xml");
435  commandline = "mytharchivehelper --logpath " + logDir + " --nativearchive "
436  "--outfile " + configDir + "/mydata.xml"; // job file
437 
440  uint retval = myth_system(commandline, flags);
441  if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
442  {
443  ShowOkPopup(tr("It was not possible to create the DVD. "
444  "An error occured when running the scripts") );
445  return;
446  }
447 
448  showLogViewer();
449 }
450 
452 {
454 
455  auto *selector = new RecordingSelector(mainStack, &m_archiveList);
456 
457  connect(selector, SIGNAL(haveResult(bool)),
458  this, SLOT(selectorClosed(bool)));
459 
460  if (selector->Create())
461  mainStack->AddScreen(selector);
462 }
463 
465 {
466  if (ok)
468 }
469 
471 {
472  MSqlQuery query(MSqlQuery::InitCon());
473  query.prepare("SELECT title FROM videometadata");
474  if (query.exec() && query.size())
475  {
476  }
477  else
478  {
479  ShowOkPopup(tr("You don't have any videos!"));
480  return;
481  }
482 
484 
485  auto *selector = new VideoSelector(mainStack, &m_archiveList);
486 
487  connect(selector, SIGNAL(haveResult(bool)),
488  this, SLOT(selectorClosed(bool)));
489 
490  if (selector->Create())
491  mainStack->AddScreen(selector);
492 }
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
void loadConfiguration(void)
void handleNextPage(void)
void SetUsed(int value)
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
void handleAddRecording(void)
void Show(void)
void titleChanged(MythUIButtonListItem *item)
void getArchiveListFromDB(void)
void saveConfiguration(void)
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
MythUIButton * m_addrecordingButton
Definition: exportnative.h:85
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
~ExportNative(void) override
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString getTempDirectory(bool showError)
Definition: archiveutil.cpp:71
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
Basic menu dialog, message and a list of options.
MythUIText * m_datetimeText
Definition: exportnative.h:89
void handleAddVideo(void)
int size(void) const
Definition: mythdbcon.h:203
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:135
MythScreenStack * GetStack(const QString &stackname)
void handleCancel(void)
void updateArchiveList(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythScreenStack * GetMainStack()
MythUIText * m_titleText
Definition: exportnative.h:88
uint m_usedSpace
Definition: exportnative.h:72
void handlePrevPage(void)
void BuildFocusList(void)
ARCHIVEDESTINATION type
Definition: archiveutil.h:28
void Hide(void)
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
MythUIText * m_currsizeText
Definition: exportnative.h:95
void updateSizeBar(void)
MythUIButtonList * m_archiveButtonList
Definition: exportnative.h:81
MythUIButton * m_addvideoButton
Definition: exportnative.h:86
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
bool m_bCreateISO
Definition: exportnative.h:76
ArchiveDestination m_archiveDestination
Definition: exportnative.h:71
bool m_bEraseDvdRw
Definition: exportnative.h:78
MythUIText * m_nofilesText
Definition: exportnative.h:92
QString GetSetting(const QString &key, const QString &defaultval="")
MythScreenType * m_previousScreen
Definition: exportnative.h:69
void selectorClosed(bool ok)
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 SetTotal(int value)
#define GENERIC_EXIT_RUNNING
Process is running.
Definition: exitcodes.h:25
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
unsigned int uint
Definition: compat.h:140
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
void createConfigFile(const QString &filename)
MythUIButton * m_nextButton
Definition: exportnative.h:82
uint myth_system(const QString &command, uint flags, uint timeout)
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
MythUIType * GetFocusWidget(void) const
MythUIButton * m_cancelButton
Definition: exportnative.h:84
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
MythMainWindow * GetMythMainWindow(void)
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
static QString formatSize(int64_t sizeKB, int prec)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
MythUIText * m_currsizeErrText
Definition: exportnative.h:96
avoid disabling UI drawing
Definition: mythsystem.h:35
QString m_saveFilename
Definition: exportnative.h:79
run child in the background
Definition: mythsystem.h:36
void showLogViewer(void)
Definition: logviewer.cpp:24
void removeItem(void)
int numRowsAffected() const
Definition: mythdbcon.h:206
void ShowMenu(void) override
MythUIText * m_filesizeText
Definition: exportnative.h:91
void SetItemCurrent(MythUIButtonListItem *item)
bool SetFocusWidget(MythUIType *widget=nullptr)
MythUIButton * m_prevButton
Definition: exportnative.h:83
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
MythUIProgressBar * m_sizeBar
Definition: exportnative.h:97
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
bool Create(void) override
MythUIButtonListItem * GetItemFirst() const
void getArchiveList(void)
MythUIText * m_maxsizeText
Definition: exportnative.h:93
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
MythUIText * m_minsizeText
Definition: exportnative.h:94
MythUIText * m_descriptionText
Definition: exportnative.h:90
QList< ArchiveItem * > m_archiveList
Definition: exportnative.h:74
MythUIButtonListItem * GetItemCurrent() const
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:41