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  for (int x = 0; x < m_archiveList.size(); x++)
135  {
136  ArchiveItem *a = m_archiveList.at(x);
137  size += a->size;
138  }
139 
140  m_usedSpace = size / 1024 / 1024;
141  uint freeSpace = m_archiveDestination.freeSpace / 1024;
142 
143  QString tmpSize;
144 
145  m_sizeBar->SetTotal(freeSpace);
147 
148  tmpSize.sprintf("%0d Mb", freeSpace);
149 
150  if (m_maxsizeText)
151  m_maxsizeText->SetText(tmpSize);
152 
153  if (m_minsizeText)
154  m_minsizeText->SetText("0 Mb");
155 
156  tmpSize.sprintf("%0d Mb", m_usedSpace);
157 
158  if (m_usedSpace > freeSpace)
159  {
160  if (m_currsizeText)
161  m_currsizeText->Hide();
162 
163  if (m_currsizeErrText)
164  {
166  m_currsizeErrText->SetText(tmpSize);
167  }
168  }
169  else
170  {
171  if (m_currsizeErrText)
173 
174  if (m_currsizeText)
175  {
176  m_currsizeText->Show();
177  m_currsizeText->SetText(tmpSize);
178  }
179  }
180 }
181 
183 {
184  ArchiveItem *a = item->GetData().value<ArchiveItem *>();
185  if (!a)
186  return;
187 
189 
190  m_datetimeText->SetText(a->startDate + " " + a->startTime);
191 
193  (a->subtitle != "" ? a->subtitle + "\n" : "") + a->description);
194 
195  m_filesizeText->SetText(formatSize(a->size / 1024, 2));
196 }
197 
199 {
200  if (m_archiveList.empty())
201  {
202  ShowOkPopup(tr("You need to add at least one item to archive!"));
203  return;
204  }
205 
206  runScript();
207 
209  Close();
210 }
211 
213 {
214  Close();
215 }
216 
218 {
220  Close();
221 }
222 
224 {
226 
227  if (m_archiveList.empty())
228  {
229  m_titleText->Reset();
233  m_nofilesText->Show();
234  }
235  else
236  {
237  for (int x = 0; x < m_archiveList.size(); x++)
238  {
239  ArchiveItem *a = m_archiveList.at(x);
240 
242  item->SetData(qVariantFromValue(a));
243  }
244 
247  m_nofilesText->Hide();
248  }
249 
250  updateSizeBar();
251 }
252 
254 {
255  while (!m_archiveList.isEmpty())
256  delete m_archiveList.takeFirst();
257  m_archiveList.clear();
258 
259  MSqlQuery query(MSqlQuery::InitCon());
260  query.prepare("SELECT intid, type, title, subtitle, description, size, "
261  "startdate, starttime, filename, hascutlist "
262  "FROM archiveitems WHERE type = 'Recording' OR type = 'Video' "
263  "ORDER BY title, subtitle");
264 
265  if (query.exec())
266  {
267  while (query.next())
268  {
269  ArchiveItem *item = new ArchiveItem;
270 
271  item->id = query.value(0).toInt();
272  item->type = query.value(1).toString();
273  item->title = query.value(2).toString();
274  item->subtitle = query.value(3).toString();
275  item->description = query.value(4).toString();
276  item->size = query.value(5).toLongLong();
277  item->startDate = query.value(6).toString();
278  item->startTime = query.value(7).toString();
279  item->filename = query.value(8).toString();
280  item->hasCutlist = (query.value(9).toInt() > 0);
281  item->useCutlist = false;
282  item->editedDetails = false;
283 
284  m_archiveList.append(item);
285  }
286  }
287 }
288 
290 {
293 }
294 
296 {
297  m_bCreateISO = (gCoreContext->GetSetting("MythNativeCreateISO", "0") == "1");
298  m_bDoBurn = (gCoreContext->GetSetting("MythNativeBurnDVDr", "1") == "1");
299  m_bEraseDvdRw = (gCoreContext->GetSetting("MythNativeEraseDvdRw", "0") == "1");
300  m_saveFilename = gCoreContext->GetSetting("MythNativeSaveFilename", "");
301 }
302 
304 {
305  // remove all old archive items from DB
306  MSqlQuery query(MSqlQuery::InitCon());
307  query.prepare("DELETE FROM archiveitems;");
308  if (!query.exec())
309  MythDB::DBError("ExportNative::saveConfiguration - "
310  "deleting archiveitems", query);
311 
312  // save new list of archive items to DB
313  query.prepare("INSERT INTO archiveitems (type, title, subtitle, "
314  "description, startdate, starttime, size, filename, hascutlist, "
315  "duration, cutduration, videowidth, videoheight, filecodec,"
316  "videocodec, encoderprofile) "
317  "VALUES(:TYPE, :TITLE, :SUBTITLE, :DESCRIPTION, :STARTDATE, "
318  ":STARTTIME, :SIZE, :FILENAME, :HASCUTLIST, :DURATION, "
319  ":CUTDURATION, :VIDEOWIDTH, :VIDEOHEIGHT, :FILECODEC, "
320  ":VIDEOCODEC, :ENCODERPROFILE);");
321  for (int x = 0; x < m_archiveList.size(); x++)
322  {
323  ArchiveItem *a = m_archiveList.at(x);
324 
325  query.bindValue(":TYPE", a->type);
326  query.bindValue(":TITLE", a->title);
327  query.bindValue(":SUBTITLE", a->subtitle);
328  query.bindValue(":DESCRIPTION", a->description);
329  query.bindValue(":STARTDATE", a->startDate);
330  query.bindValue(":STARTTIME", a->startTime);
331  query.bindValue(":SIZE", 0);
332  query.bindValue(":FILENAME", a->filename);
333  query.bindValue(":HASCUTLIST", a->hasCutlist);
334  query.bindValue(":DURATION", 0);
335  query.bindValue(":CUTDURATION", 0);
336  query.bindValue(":VIDEOWIDTH", 0);
337  query.bindValue(":VIDEOHEIGHT", 0);
338  query.bindValue(":FILECODEC", "");
339  query.bindValue(":VIDEOCODEC", "");
340  query.bindValue(":ENCODERPROFILE", "");
341 
342  if (!query.exec())
343  MythDB::DBError("archive item insert", query);
344  }
345 }
346 
348 {
349  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
350 
351  MythDialogBox *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
352 
353  if (menuPopup->Create())
354  popupStack->AddScreen(menuPopup);
355 
356  menuPopup->SetReturnEvent(this, "action");
357 
358  menuPopup->AddButton(tr("Remove Item"), SLOT(removeItem()));
359 }
360 
362 {
364  ArchiveItem *curItem = item->GetData().value<ArchiveItem *>();
365 
366  if (!curItem)
367  return;
368 
369  MSqlQuery query(MSqlQuery::InitCon());
370  query.prepare("DELETE FROM archiveitems WHERE filename = :FILENAME;");
371  query.bindValue(":FILENAME", curItem->filename);
372  if (query.exec() && query.numRowsAffected())
373  {
374  getArchiveList();
375  }
376 }
377 
378 void ExportNative::createConfigFile(const QString &filename)
379 {
380  QDomDocument doc("NATIVEARCHIVEJOB");
381 
382  QDomElement root = doc.createElement("nativearchivejob");
383  doc.appendChild(root);
384 
385  QDomElement job = doc.createElement("job");
386  root.appendChild(job);
387 
388  QDomElement media = doc.createElement("media");
389  job.appendChild(media);
390 
391  // now loop though selected archive items and add them to the xml file
392  for (int x = 0; x < m_archiveList.size(); x++)
393  {
394  ArchiveItem *a = m_archiveList.at(x);
395 
396  QDomElement file = doc.createElement("file");
397  file.setAttribute("type", a->type.toLower() );
398  file.setAttribute("title", a->title);
399  file.setAttribute("filename", a->filename);
400  file.setAttribute("delete", "0");
401  media.appendChild(file);
402  }
403 
404  // add the options to the xml file
405  QDomElement options = doc.createElement("options");
406  options.setAttribute("createiso", static_cast<int>(m_bCreateISO));
407  options.setAttribute("doburn", static_cast<int>(m_bDoBurn));
408  options.setAttribute("mediatype", m_archiveDestination.type);
409  options.setAttribute("dvdrsize", (qint64)m_archiveDestination.freeSpace);
410  options.setAttribute("erasedvdrw", static_cast<int>(m_bEraseDvdRw));
411  options.setAttribute("savedirectory", m_saveFilename);
412  job.appendChild(options);
413 
414  // finally save the xml to the file
415  QFile f(filename);
416  if (!f.open(QIODevice::WriteOnly))
417  {
418  LOG(VB_GENERAL, LOG_ERR,
419  QString("ExportNative::createConfigFile: "
420  "Failed to open file for writing - %1") .arg(filename));
421  return;
422  }
423 
424  QTextStream t(&f);
425  t << doc.toString(4);
426  f.close();
427 }
428 
430 {
431  QString tempDir = getTempDirectory();
432  QString logDir = tempDir + "logs";
433  QString configDir = tempDir + "config";
434  QString commandline;
435 
436  // remove any existing logs
437  myth_system("rm -f " + logDir + "/*.log");
438 
439  // remove cancel flag file if present
440  if (QFile::exists(logDir + "/mythburncancel.lck"))
441  QFile::remove(logDir + "/mythburncancel.lck");
442 
443  createConfigFile(configDir + "/mydata.xml");
444  commandline = "mytharchivehelper --logpath " + logDir + " --nativearchive "
445  "--outfile " + configDir + "/mydata.xml"; // job file
446 
449  uint retval = myth_system(commandline, flags);
450  if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
451  {
452  ShowOkPopup(tr("It was not possible to create the DVD. "
453  "An error occured when running the scripts") );
454  return;
455  }
456 
457  showLogViewer();
458 }
459 
461 {
463 
464  RecordingSelector *selector = new RecordingSelector(mainStack, &m_archiveList);
465 
466  connect(selector, SIGNAL(haveResult(bool)),
467  this, SLOT(selectorClosed(bool)));
468 
469  if (selector->Create())
470  mainStack->AddScreen(selector);
471 }
472 
474 {
475  if (ok)
477 }
478 
480 {
481  MSqlQuery query(MSqlQuery::InitCon());
482  query.prepare("SELECT title FROM videometadata");
483  if (query.exec() && query.size())
484  {
485  }
486  else
487  {
488  ShowOkPopup(tr("You don't have any videos!"));
489  return;
490  }
491 
493 
494  VideoSelector *selector = new VideoSelector(mainStack, &m_archiveList);
495 
496  connect(selector, SIGNAL(haveResult(bool)),
497  this, SLOT(selectorClosed(bool)));
498 
499  if (selector->Create())
500  mainStack->AddScreen(selector);
501 }
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
void loadConfiguration(void)
void handleNextPage(void)
avoid disabling UI drawing
Definition: mythsystem.h:35
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
void handleAddRecording(void)
void Show(void)
void titleChanged(MythUIButtonListItem *item)
void getArchiveListFromDB(void)
~ExportNative(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
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
void SetData(QVariant data)
bool hasCutlist
Definition: archiveutil.h:69
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.
MythUIText * m_datetimeText
Definition: exportnative.h:89
run child in the background
Definition: mythsystem.h:36
void handleAddVideo(void)
int size(void) const
Definition: mythdbcon.h:203
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:136
MythScreenStack * GetStack(const QString &stackname)
void handleCancel(void)
void updateArchiveList(void)
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool Create() override
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
QString startTime
Definition: archiveutil.h:59
QString filename
Definition: archiveutil.h:60
void Hide(void)
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
MythUIText * m_currsizeText
Definition: exportnative.h:95
void AddButton(const QString &title, QVariant data=0, bool newMenu=false, bool setCurrent=false)
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
bool Create() override
QString GetSetting(const QString &key, const QString &defaultval="")
MythScreenType * m_previousScreen
Definition: exportnative.h:69
void selectorClosed(bool ok)
unsigned char t
Definition: ParseText.cpp:329
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
#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
static MSqlQueryInfo InitCon(ConnectionReuse=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
QString type
Definition: archiveutil.h:54
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)
QString subtitle
Definition: archiveutil.h:56
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:807
bool keyPressEvent(QKeyEvent *) override
Key event handler.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
MythUIText * m_currsizeErrText
Definition: exportnative.h:96
QString startDate
Definition: archiveutil.h:58
QString title
Definition: archiveutil.h:55
void SetReturnEvent(QObject *retobject, const QString &resultid)
QString m_saveFilename
Definition: exportnative.h:79
void showLogViewer(void)
Definition: logviewer.cpp:24
void removeItem(void)
int numRowsAffected() const
Definition: mythdbcon.h:206
QString description
Definition: archiveutil.h:57
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
int64_t size
Definition: archiveutil.h:61
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
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
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
bool Create(void) override
bool keyPressEvent(QKeyEvent *) override
Key event handler.