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>
26
27// mytharchive
28#include "archiveutil.h"
29#include "exportnative.h"
30#include "fileselector.h"
31#include "logviewer.h"
32#include "recordingselector.h"
33#include "videoselector.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
80
81
85
88
90
92
94
95 return true;
96}
97
98bool 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 const 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 {
122 handled = false;
123 }
124 }
125
126 if (!handled && MythScreenType::keyPressEvent(event))
127 handled = true;
128
129 return handled;
130}
131
133{
134 int64_t size = 0;
135
136 for (const auto *a : std::as_const(m_archiveList))
137 size += a->size;
138
139 m_usedSpace = size / 1024 / 1024;
140 uint freeSpace = m_archiveDestination.freeSpace / 1024;
141
142 m_sizeBar->SetTotal(freeSpace);
144
145 QString tmpSize = QString("%1 Mb").arg(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 = QString("%1 Mb").arg(m_usedSpace);
154
155 if (m_usedSpace > freeSpace)
156 {
157 if (m_currsizeText)
159
161 {
163 m_currsizeErrText->SetText(tmpSize);
164 }
165 }
166 else
167 {
170
171 if (m_currsizeText)
172 {
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
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 {
231 }
232 else
233 {
234 for (auto *a : std::as_const(m_archiveList))
235 {
236 auto* item = new MythUIButtonListItem(m_archiveButtonList, a->title);
237 item->SetData(QVariant::fromValue(a));
238 }
239
243 }
244
246}
247
249{
250 while (!m_archiveList.isEmpty())
251 delete m_archiveList.takeFirst();
252 m_archiveList.clear();
253
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
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 for (const auto * a : std::as_const(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"), &ExportNative::removeItem);
352}
353
355{
357 auto *curItem = item->GetData().value<ArchiveItem *>();
358
359 if (!curItem)
360 return;
361
363 query.prepare("DELETE FROM archiveitems WHERE filename = :FILENAME;");
364 query.bindValue(":FILENAME", curItem->filename);
365 if (query.exec() && query.numRowsAffected())
366 {
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 for (const auto * a : std::as_const(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
449}
450
452{
454
455 auto *selector = new RecordingSelector(mainStack, &m_archiveList);
456
457 connect(selector, &RecordingSelector::haveResult,
459
460 if (selector->Create())
461 mainStack->AddScreen(selector);
462}
463
465{
466 if (ok)
468}
469
471{
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, &VideoSelector::haveResult,
489
490 if (selector->Create())
491 mainStack->AddScreen(selector);
492}
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)
unsigned int uint
Definition: compat.h:68
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
@ GENERIC_EXIT_RUNNING
Process is running.
Definition: exitcodes.h:28
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