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